• 締切済み

イベント登録とメモリリークについて(jQuery)

下記のような【html】をjQueryのAjax関数とhtml()を利用した【処理フロー】で、動的にDOMに追加しているのですが、以下の【質問】にお答え頂けないでしょうか。 【html】 <div> (省略) <a href="javascript:void(0);" onclick="foo({'arg1':'str1','arg2':'str2','arg3':num3…});">テスト</a> (省略) </div> ※foo関数の引数は初期化配列を指定、引数値はサーバサイドで動的に変化させてクライアントに出力 【処理フロー】 a要素クリック→foo関数実行→jQueryのAjax関数でfoo関数引数の配列値をサーバに送信→レスポンスとして新たな【html】を受信して、jQueryのempty()、html()を使用して同じ要素配下を置き換え ※$.ajax({..., data:配列オブジェクト, ...}); のように、dataプロパティにfoo関数の引数をそのまま渡しています 【質問】 1.上記のonclickの内容が原因かどうかは分かっていませんが、繰り返しフローを走らせると、各ブラウザ(IE8,FireFox3.6,safari5,Opera11)でメモリリークが発生します。上記のonclickの関数に初期化配列という定義に問題はあるでしょうか。 2.foo関数の引数にある配列は、要素がクリックされたタイミングでオブジェクト化される認識であっていますでしょうか。また、上記のフローだと開放される(参照されなくなる)タイミングは、Ajax通信完了後という認識であっていますでしょうか。 3.Webで調査した範囲では、jQueryのempty()、html()はどちらも削除/置き換え前の要素に紐づいたイベントは削除されるようなのですが、上記のようにhtml()で追加する要素にonclickイベントを定義している場合も、次回empty()、html()で置き換え時にイベント削除されるでしょうか。 4.1に関連しますが、上記の内容、処理フローにおいて、置き換え前の要素セットやfoo関数の引数がメモリリークを引き起こす可能性はあるでしょうか。 どうぞよろしくお願いいたします。

みんなの回答

回答No.2

私が最近確認したのはWindows版IE7、IE8、Firefox3.5、3.6、Safari3、5、Opera11ですが、 昔確認したのを入れると、IE6、Firefox1.5、2.0、3.0、Opera8.0.2、9.5、9.6、10.0です。 上記全てのブラウザ・バージョンで、メモリが解放されるタイミング(ガーベッジコレクション実行)は、「URI変更時」です。 (そういえば、location.hrefではチェックしましたが、location.replaceではチェックしていません。) フルAjaxサイトではガーベッジコレクション実行されませんから、自分でメモリを解放するための仕組みを付ける必要があります。 AmazonやYahoo、GoogleくらいのAjaxの使い方なら、頻繁にURI変更が行われるので、気にするレベルではない(ガーベッジコレクションに任せても大丈夫)と思います。 >1.上記のonclickの内容が原因かどうかは分かっていませんが、繰り返しフローを走らせると、各ブラウザ(IE8,FireFox3.6,safari5,Opera11)でメモリリークが発生します。上記のonclickの関数に初期化配列という定義に問題はあるでしょうか。 初期化配列というのが何なのかわかりませんが、 element.onclick=function(){}; でイベントハンドラを指定している場合、innerHTMLやDOMの.removeChild()や.replaceChild()で置き換えた場合は、 あらかじめelement.onclick=null;または.removeEventListener()などでハンドラを除去する必要があります。 foo関数を呼び出したときではなく、 >jQueryのempty()、html()を使用して同じ要素配下を置き換え この処理の時にメモリリークが発生していると考えた方が自然だと思います。 頻繁にURIを変更するのであれば、多少のメモリリークは(ガーベッジコレクションで直るので)無視して大丈夫だと思います。 >2.foo関数の引数にある配列は、要素がクリックされたタイミングでオブジェクト化される認識であっていますでしょうか。 合っています。 >また、上記のフローだと開放される(参照されなくなる)タイミングは、Ajax通信完了後という認識であっていますでしょうか。 foo関数の引数の分は、関数を抜けたタイミングで解放されます。 ただし、関数から別の関数を呼び出すときに引数として使用した物が、参照渡し(String、Number以外)になっている場合は、ガーベッジコレクションを待つことがあるようです。 (私が調べた中では、メモリリークを起こす詳しい条件が不明。新しいブラウザほどリークしなくなっているようです。) これは「クロージャ」が関係しているようです。 delete object;またはobject=null;を行っておけばメモリは解放されます。 Ajax通信に使用した変数(XHRオブジェクトに通信データとして渡した物)は、XHRオブジェクトを削除するか、ガーベッジコレクションが働くまでメモリ内に残るようです。 そのため、XHRオブジェクトの削除をガーベッジコレクションに任せている場合は、 URIを移動しない限りメモリを消費し続けるようです。 ですが、XHRオブジェクトを使い回しするような場合などでは、 2回目移行の通信では「それ以上メモリを消費しない」という意味で、メモリは解放されなくてもメモリリークしていないと(私は)考えます。 (グローバル変数がメモリを解放されないのと同じ) >3.Webで調査した範囲では、jQueryのempty()、html()はどちらも削除/置き換え前の要素に紐づいたイベントは削除されるようなのですが、上記のようにhtml()で追加する要素にonclickイベントを定義している場合も、次回empty()、html()で置き換え時にイベント削除されるでしょうか。 わかりません。 私が確認した中では、jQueryだけでHTMLを書き換えた方法では、完全にメモリリークを抑えられませんでした。 もちろん、私のjQueryの使い方が間違っている可能性もあります。

takt_s
質問者

お礼

詳細な情報をありがとうございます、大変助かります。 ○ガベージコレクションについて タイミングについて知りませんでした。今回ほぼ、フルAjaxサイトを構築しているため、必要だと思われる箇所でガベージコレクション任せで変数にnullをいれていたのですが、URI変更しないとなるときっとダメなのですね。deleteその他、メモリ解放の手段を調べてみます。 ○ハンドラ除去について innerHTMLや.removeChild()ではあらかじめ除去ということは認識していたのですが、jQueryのhtml()を使用する場合は、innerHTMLなどと同じ処理が行われるのかjQueryのbind()を経由してイベント登録されるのか、といったところが分からずにいました。 もし後者であれば、仕様を見る限りempty()、html()などで一括除去してくれるものだと安易に考えていましたが、追加されるhtmlの内容を解析してbind()まではjQueryもしていないように思い改めました。一応jQueryのソースを調べみますが、ご教示頂いた方法で除去することにします。 ○メモリリークについて メモリリークを起こす条件が私も分からないのです。新しいブラウザほど、というのもこちらで確認済みです。 参照渡しになっているものが多いため、おそらく前述でおっしゃっていたURI変更をしていないためガベージコレクションが働いていなかったことも原因の1つだと思います。ただ、参照渡しの変数全て処理フロー最後にnullをいれた状態で、時々URI変更する操作を行っていたこともありますが、それでもリークしていったため、今回は別の問題も含んでいると思っています。クロージャ含めコードを見直してみます。 ○XHRオブジェクトについて そうなのですね、ここが一番調査余地がありそうです。XHRオブジェクトの使いまわしはしておりませんし、DOMの大部分を置き換えるような通信をした場合に2,3MB単位でメモリ使用量が増えることが多々あるため、受信したデータがメモリ上に溜まっていってるのかもしれません。 ご回答頂いて気付いた点を中心に引き続き調べてみます。 私の知識が不足し過ぎているようでお手数をおかけします。

  • gorusura
  • ベストアンサー率59% (25/42)
回答No.1

http://semooh.jp/jquery/api/ajax/jQuery.ajax/options/ asyncをfalseにして、同期通信にしては如何でしょうか? 通信中は何もできませんが、通信中にクリックされることを防ぐことが出来ます。

takt_s
質問者

お礼

今回は非同期が必須ですが、問題の切り分けになるかもしれませんので、同期の状態でも調べてみます。ご回答ありがとうございます。

関連するQ&A

  • jqueryのajaxに引数を指定

    ajaxを利用して外部HTMLを読み込ませたいのですが$.ajax()などをonclickイベントなどで呼び出すとき引数を指定してurlを設定することはできませんか イメージとしては 例えばjqueryでloadingpageという関数を作って HTMLを <input type="button" onclick="loadingpage(http://www.hogehoge.com);"> って感じにしてボタンをクリックするとhttp://www.hogehoge.comがページ内で読みだされるようにしたいです。

  • jQueryでの、引数を取るJS関数の呼出方法

    jQueryの学習をしています。 HTML & JavaScriptで下記のように書かれているコード <input type=button id=button1 onclick=func_a() /> <script> function func1(){ //処理いろいろ } </script> は、jQueryを使って書くと、 $('#button1').live('click', func_a); このように書けます。 <input type=button id=button1 onclick=func_b(arg1, arg2) /> <script> function func_b(argument1, argument2){ //処理いろいろ } のように引数を取る関数は jQueryではどのように書けばよいのでしょうか? $('#button1').live('click', func_b(arg1, arg2)); とは書けなかったので、今は、 if (arg1 == xxx){ $('#button1').live('click', func_b_1_1); } else { $('#button1').live('click', func_b_1_2); } などのように引数別に同じような関数を創って書いています。 DRY原則という同じコードを二度書くなという約束もあるようなので、 きっといい方法があると思うのですが、 お詳しい方、教えていただけるとうれしいです。 どうぞ、よろしくお願いいたします。

  • jQueryのイベントに引数を渡したい

    例えば以下のような既存のJavaScriptのコードをjQueryのイベントで書き直したい場合、関数の引数で渡していた値は、どうやって渡すのでしょうか? 【html】 <a href="" onclick="func_a('abc'); return false;"></a> 【JavaScript】 faunction func_a(param_a){ alert(param_a); } 以下のようにすれば渡せないことはありませんが、かなり強引な感じがします。 【html】 <a href="" class="abc"></a> 【jQuery】 $(function() { $("a").click( function(){ var param = $(this).attr("class"); alert(param); return false; } ); }); こういう場合はjQeuryであっても、onclick属性で関数を呼び出すのが普通なのでしょうか? ご存知の方がいらっしゃいましたら教えてください。 よろしくお願い申し上げます。

  • Jqueryのイベントが動かないケースがある

    Jqueryを使ってイベントによる実行をしています。 上手く行くケース html読み込み時にあるi要素のクリックイベント に関しては問題なく実行されました。 hogehogeRegisterEvent : function(container){ var thisInstance = this; container.find('.iで指定した名称').on("click",function(e){ 処理; }); その他処理 }, という感じで指定したところできました。 それを ボタンクリックをした時にjavascriptにて、i要素を作成してそれをクリックするとイベントが発生しません。 挿入処理は var cell = row.insertCell(2); cell.innerHTML = "i要素を挿入"; という感じでテーブル内部にinsertCellで行っています。 そもそもこの方法だと上手く認識してくれないものなのでしょうか? 上手く認識させるための要素挿入方法もしくは、イベントをリフレッシュするなどして追加したコード部分も認識させる方法を教えてください。

  • jQueryの導入について

    お世話になります。 PHPでサイトを構築しているのですが、 AJAXを導入したくてjQueryのAJAXライブラリを使おうと思っております。 jQueryは従来のJavaScriptとは違い、 構文の書き方やイベント処理等が、 独特な書き方になると思います。 jQueryを使用する際は、 すべてのJavaScriptをjQueryの方式で構文を書くか、 必要箇所だけjQueryを使用し、 あとは従来のJavaScriptの方式で構文を書くか悩んでいます。 今後どちらの方が良いでしょうか。 ちなみに私はすべての構文をjQuery風に書いた方が、 スマートになるかなと思っております。 よろしくお願い致します。

  • jQuery.eachは第2引数以上は作れないのでしょうか?

    jQuery.eachは第2引数以上は作れないのでしょうか? 配列として3つ設定したい場合はできないものなのでしょうか? var list = { "#load1": "A.html .ld1":"aaa1", "#load2": "B.html .ld2":"bbb2", "#load3": "C.html .ld3":"ccc3" }; jQuery.each(list, function(key,val, arg) { $(key).load(val, function(Text, status) { if ($(key).text() == "A") $(arg).replaceWith("<img src='baby.png'/>"); }); }); こんな書き方は出来なかったので・・・ 第3引数があるか調べてみたものの第2までしか書いていないのでないと思っています。 しかし3つ以上の場合はどのようにしたらいいのでしょうか?

  • IntArrayクラスのプログラムを組んでいるのですが・・・

    javaプログラミングで以下の内容を満たす、IntArrayクラスを作成しています。 ・インスタンス変数  int型の配列data ・コンストラクタ  (1)int型の配列を受け取り、そのコピーを内部的に保持する  (2)第1引数で指定された要素数を持つ配列を確保し、全ての要素に初期値として第2引数で指定された値をセットする  (3)第1引数で指定された要素数を持つ配列を確保し、全ての要素に初期値としてゼロをセットする ・メソッド  (1)sort   内部的に保持している配列を、引数の値がtrueであれば昇順、falseであれば降順にソートする  (2)length   IntArrayが保持している配列の要素数を取得する  (3)getElement   引数に指定された要素番号の値を取得する  (4)setElement   第1引数に指定された要素番号に第2引数で指定された値を格納する   (5)getArray   IntArrayが保持している配列のコピーを取得する 実際にプログラムを組んでみたのですが、※の部分が冗長だと指摘を受けました。 しかしどのように修正したらよいかわかりません。 どうかアドバイスなどをよろしくお願いいたします。 package java_Lesson; import java.util.Arrays; /** * int型の配列dataの要素を取得、あるいは操作するクラス */ class IntArray { private int[] data; /** * int型の配列を受け取り、そのコピーを内部的に保持する * * @param args コピー元となる配列 */ IntArray(int[] args) { data = (int[])args.clone(); } /** * 第1引数で指定された要素数を持つ配列を確保し、 * 全ての要素に初期値として第2引数で指定された値をセットする * * @param arg1 配列の要素数 * @param arg2 配列の初期値 */ IntArray(int arg1, int arg2) { this(arg1); /* ※ */ for (int index = 0; arg1 > index; index ++) { data[index] = arg2; } } /** * 第1引数で指定された要素数を持つ配列を確保し、 * 全ての要素に初期値としてゼロをセットする * * @param arg 配列の要素数 */ IntArray(int arg) { data = new int[arg]; } /** * 内部的に保持している配列を昇順、あるいは降順にソートする * * @param arg trueであれば昇順、falseであれば降順にソート */ void sort(boolean arg) { Arrays.sort(data); if (arg) { return; } int array = data.length - 1; for (int index = 0; array > index; index ++, array --) { int temp = data[index]; data[index] = data[array]; data[array] = temp; } } /** * IntArrayが保持している配列の要素数を取得する * * @return 配列の要素数 */ int length() { return data.length; } /** * 引数に指定された要素番号の値を取得する * * @param arg 返す要素の要素番号 * * @return 配列dataの要素番号argの値 */ int getElement(int arg) { return data[arg]; } /** * 第1引数に指定された要素番号に第2引数で指定された値を格納する * * @param arg1 値を格納する要素番号 * @param arg2 配列に格納する値 */ void setElement(int arg1, int arg2) { data[arg1] = arg2; } /** * IntArrayが保持している配列のコピーを取得する * * @return 配列dataのコピー */ int[] getArray() { return data.clone(); } }

    • ベストアンサー
    • Java
  • SELECT要素のjQueryイベントハンドラ

    SELECT要素のドロップダウンリストに対して、 選択項目が変わった時のイベントを設定したいと考えています。 jQueryを利用しているのですが、$("#...").change(func) による設定では、 キーボードで項目を変更した時に実行されませんでした。 他はjQuery利用で統一しているので出来るだけjQueryを使いたいのですが、 これはjQueryの仕様的に対応していないのでしょうか? keydownを拾って別途キーボード操作を処理すれば解決はできますが・・・

  • イベントに独自引数を渡したい。

    ACTIONSCRIPT3.0で、外部ファイルを読み込ませる処理を行っているのですが、読み込み完了時の関数に独自の引数を渡すことは不可能なのでしょうか? var j:int = 100; var myArray:Array; for(var i=0;i<j;i++){  myArray[i] = new Loader();  myArray[i].load(new URLRequest(別の配列に入っている文字列のURL))  myArray[i].contentLoaderInfo.addEventListener(Event.INIT,myfunc); } function myfunc(e){イベント発生時の処理} のように、多数のイベント処理を書くときに配列のインデックスによって処理を分けたいのですが、上の例だとイベント発生時の関数であるmyfuncの引数は規定オブジェクトなので独自のものが渡せません。 いまは別にグローバルな配列を作成して処理をしているのですが、メモリ常駐になることや、オブジェクト指向として気に入らない状態です。 イベントが発生したオブジェクトの名前をキーにすることも考えましたが、どの道スマートではない。なにか方法はないものでしょうか? また、言語仕様的にやむをえないのでしょうか?

    • ベストアンサー
    • Flash
  • jQueryで複数あるUL要素の最後のLI要素以外を処理したい

    jQueryで複数あるUL要素の最後のLI要素以外を処理したい 現在、HTMLを <ul> <li>foo</li> <li>bar</li> <li>com</li> </ul> <ul> <li>foo</li> <li>bar</li> <li>com</li>(※) </ul> 上記のように記述しています。 今回、jQueryを使い、2カ所あるUL要素の最後のLI要素以外を処理したいと思い、 $(function(){ $("ul li:not(:last)").css("~","~"); }); 以上のようにjQueryを記述したところ、1カ所目の最後のLI要素は無視されCSSが追加されてしまいました。 1カ所目・2カ所目とも同じように処理するにはどうしたら良いでしょうか? ご指導・ご鞭撻のほど、よろしくお願いします。

    • ベストアンサー
    • AJAX