JavaScriptガベージコレクションとは何ですか?


回答:


192

Eric Lippertがしばらく前にこのテーマに関する詳細なブログ投稿を書きました(さらにVBScriptと比較しています)。より正確には、JavaScriptによく似ていますが、ECMAScriptのMicrosoft独自の実装であるJScriptについて書いています。Internet ExplorerのJavaScriptエンジンの動作の大部分は同じであると想定できると思います。もちろん、実装はブラウザーによって異なりますが、多くの一般的な原則を採用して他のブラウザーに適用できると思います。

そのページから引用:

JScriptは非世代のマークアンドスイープガベージコレクターを使用します。それはこのように動作します:

  • 「スコープ内」にあるすべての変数は「スカベンジャー」と呼ばれます。スカベンジャーは、数値、オブジェクト、文字列などを参照できます。スカベンジャーのリストを保持しています-変数は、スコープに入るとscavリストに移動し、スコープから出るとscavリストから移動します。

  • 時々、ガベージコレクターが実行されます。まず、すべてのオブジェクト、変数、文字列など、GCによって追跡されるすべてのメモリに「マーク」を付けます。(JScriptはVARIANTデータ構造を内部で使用し、その構造には未使用のビットがたくさんあるため、そのうちの1つを設定します。)

  • 次に、スカベンジャーのマークと、スカベンジャーリファレンスの推移的な閉鎖をクリアします。したがって、スカベンジャーオブジェクトが非スカベンジャーオブジェクトを参照する場合、非スカベンジャーのビットとそれが参照するすべてのビットをクリアします。(私は以前の投稿とは異なる意味で「閉鎖」という言葉を使用しています。)

  • この時点で、まだマークされているすべてのメモリが割り当てられたメモリであり、スコープ内の変数からのパスでは到達できないことがわかります。これらのオブジェクトはすべて、自分自身を破棄するように指示され、循環参照を破棄します。

ガベージコレクションの主な目的は、プログラマーが作成して使用するオブジェクトのメモリ管理について心配しないようにすることですが、もちろんそれを回避することはできません。 。

歴史的注記:回答の以前の改訂では、delete演算子への誤った参照がありました。JavaScript では、delete演算子はオブジェクトからプロパティを削除delete、C / C ++ とはまったく異なります。


27
Appleガイドには欠陥がdeleteあります。たとえば、最初の例では、ではなく、delete foo最初にイベントリスナーを削除してからwindow.removeEventListener()、を使用foo = nullして変数を上書きする必要があります。IEではdelete window.foo(グローバルではないがdelete foo)機能しましたfooが、それでもFFやOperaでは機能しませんでした
Christoph

3
エリックの記事は「歴史的な目的のみ」と見なされるべきであることに注意してください。しかし、それはまだ有益です。
Peter Ivan

2
また、注-IE 6および7では、非世代のマークアンドスイープガベージコレクターを使用しないでください。単純な参照カウントガベージコレクタを使用します。ガベージコレクタは、ガベージコレクションに関する循環参照の問題に対してより脆弱です。
Doug

1
ECMAScript deleteは単項演算子(式)であり、ステートメント(つまり:)ではありませんdelete 0, delete 0, delete 3。式ステートメントで表現すると、ステートメントのように見えます。
Hydroper、

ええ、当時の答えは今では時代遅れです。2012年の時点で、最新のブラウザーはマーク/スイープアルゴリズムを使用しています。そのため、スコープに依存しなくなりました。参照:developer.mozilla.org/en-US/docs/Web/JavaScript/...
sksallaj

52

DOMオブジェクトが関係する場合は循環参照に注意してください。

JavaScriptのメモリリークパターン

オブジェクトへのアクティブな参照がない場合にのみメモリを再利用できることに注意してください。一部のJSエンジンは実際に内部関数で参照されている変数をチェックせず、囲んでいる関数のすべてのローカル変数を保持するため、これはクロージャーとイベントハンドラーの一般的な落とし穴です。

以下に簡単な例を示します。

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

単純なJS実装はbigString、イベントハンドラーが存在する限り収集できません。この問題を解決するにはいくつかの方法、例えば設定があるbigString = nullの終わりにinit()deleteローカル変数や関数の引数では動作しません:deleteオブジェクトから削除しプロパティ、および変数オブジェクトにアクセスできないが- strictモードでES5でもスローされますReferenceError、あなたがしようとした場合ローカル変数を削除するには!)

メモリの消費を気にする場合は、不要なクロージャをできるだけ避けることをお勧めします。


20
DOM循環参照のバグはJScriptに固有のものです。IE以外のブラウザでは影響を受けません。実際、私はECMAScript仕様で、GCがそのようなサイクルを処理できる必要があると明示的に述べていることはかなり確信しています:-/
olliej

@olliej:ECMAScript仕様に GCについての言及はありません。
Janus Troelsen 2013年


16

ブログから引用した良い引用

DOMコンポーネントは、JScriptコンポーネントと同様に「ガベージコレクション」されます。つまり、どちらかのコンポーネント内にオブジェクトを作成し、そのオブジェクトの追跡を失った場合、最終的にクリーンアップされます。

例えば:

function makeABigObject() {
var bigArray = new Array(20000);
}

その関数を呼び出すと、JScriptコンポーネントは、関数内でアクセス可能なオブジェクト(bigArrayという名前)を作成します。ただし、関数が戻るとすぐに、それを参照する方法がないため、bigArrayの「トラックを失います」。さて、JScriptコンポーネントは、それを追跡できなくなったことを認識し、bigArrayがクリーンアップされ、そのメモリが解放されます。同じようなことがDOMコンポーネントでも機能します。と言うdocument.createElement('div')か、それに似たものであれば、DOMコンポーネントがオブジェクトを作成します。どういうわけかそのオブジェクトを追跡できなくなると、DOMコンポーネントは関連するオブジェクトをクリーンアップします。


13

私の知る限り、JavaScriptのオブジェクトは、オブジェクトへの参照が残っていない場合、定期的にガベージコレクションされます。これは自動的に行われる処理ですが、C ++レベルでそれがどのように機能するかについてさらに詳しく知りたい場合は、WebKitまたはV8のソースコードを確認することをお勧めします。

通常、それについて考える必要はありませんが、IE 5.5やIE 6の初期バージョンなどの古いブラウザーでは、おそらく現在のバージョンでは、クロージャーは循環チェックを作成し、チェックされていない場合は最終的にメモリを消費します。クロージャーについて私が意味する特定のケースでは、それは、DOMオブジェクトへのJavaScript参照と、JavaScriptオブジェクトを参照するDOMオブジェクトへのオブジェクトを追加したときでした。基本的にそれを収集することはできず、最終的にループしてクラッシュを作成したテストアプリでOSが不安定になります。実際にはこれらのリークは通常小さなものですが、コードをクリーンに保つには、DOMオブジェクトへのJavaScript参照を削除する必要があります。

通常、特にモバイルWeb開発では、deleteキーワードを使用して、受け取ったJSONデータなどの大きなオブジェクトを即座に逆参照し、それを使用して必要なことをすべて実行することをお勧めします。これにより、GCの次のスイープがそのオブジェクトを削除し、そのメモリを解放します。


JavaScript-> DOM-> JavaScript循環参照の問題は、新しいバージョンのIEで解決されていますか?もしそうなら、いつから?アーキテクチャ的には非常に深く、修正される可能性は低いと思いました。情報源はありますか?
erikkallen 2009年

逸話的に。壊れたモードではなく、標準モードで実行しているIE 8の異常なリークに気づきませんでした。応答を調整します。
Heat Miser、

1
@erikkallen:はい、古いバージョンでは非常に単純なガベージコレクションアルゴリズムを使用していたため、GCのバグはIEバージョン8以降で修正されました。新しいmark-and-sweepスタイルのアルゴリズムがこれを処理します。
クマーハーシュ2014年

6

ガベージコレクション(GC)は、不要になったオブジェクトを削除することによる自動メモリ管理の形式です。

メモリを処理するプロセスはすべて、次の手順に従います。

1-必要なメモリ領域を割り当てます

2-いくつかの処理を行います

3-このメモリ空間を解放する

不要になったオブジェクトを検出するために使用される2つの主要なアルゴリズムがあります。

参照カウントガベージコレクション:このアルゴリズムは、「オブジェクトはもう必要ありません」の定義を「オブジェクトはそれを参照する他のオブジェクトを持たない」に減らします。参照ポイントがない場合、オブジェクトは削除されます

マークアンドスイープアルゴリズム:各オブジェクトをルートソースに接続します。オブジェクトはルートや他のオブジェクトに接続しません。このオブジェクトは削除されます。

現在、2番目のアルゴリズムを使用する最新のブラウザーのほとんど。


1
:そしてこのソースを追加するために、MDN参照developer.mozilla.org/en-US/docs/Web/JavaScript/...
Xenosの

4

「コンピューターサイエンスでは、ガベージコレクション(GC)は自動メモリ管理の一種です。ガベージコレクター(または単にコレクター)は、アプリケーションによって再度アクセスまたは変更されることのないオブジェクトによって使用されるガベージまたはメモリを再利用しようとします。」

すべてのJavaScriptエンジンには独自のガベージコレクターがあり、異なる場合があります。彼らは彼らがすることになっていることをするだけなので、ほとんどの場合あなたはそれらに対処する必要はありません。

より優れたコードを書くには、プログラミングの原則、言語、特定の実装をどれだけ知っているかにかかっています。


1

JavaScriptガベージコレクションとは何ですか?

これをチェック

より良いコードを書くために、WebプログラマーがJavaScriptガベージコレクションについて理解するために何が重要ですか?

Javascriptでは、メモリの割り当てと割り当て解除は関係ありません。問題全体はJavascriptインタプリタに要求されます。リークはまだJavaScriptで可能ですが、インタプリタのバグです。このトピックに興味がある場合は、www.memorymanagement.orgで詳細を読むことができます。


リンクしている記事のさまざまなメモリ管理システムのうち、JavaScriptで使用されているものはどれですか。「リークはまだJavaScriptで可能ですが、インタープリターのバグです。」-これは、JSプログラマーが問題全体を単に無視できることを意味するものではありません。たとえば、古いバージョンのIEにはJSコードで回避できるかなり有名なJS <-> DOM循環参照の問題があります。また、JSクロージャーの動作は設計上の機能であり、バグではありませんが、クロージャーを「不適切に」使用すると、意図したよりも大きなメモリチャンクを占有する可能性があります(「emを使用しない」とは言っていません)。
nnnnnn 2012

3
メモリリークはJavaScriptの獣です。単純な「大学プロジェクト」アプリケーションを作成している場合は、心配する必要はありません。しかし、高性能なエンタープライズレベルのアプリを書き始めるときは、JavaScriptでのメモリ管理が必須です。
Doug

1

Windowsでは、Drip.exeを使用してメモリリークを検出したり、無料のmemルーチンが機能するかどうかを確認したりできます。

とても簡単です。ウェブサイトのURLを入力するだけで、統合されたIEレンダラーのメモリ消費量を確認できます。次に、更新を押します。メモリが増加した場合、Webページのどこかにメモリリークが見つかりました。しかし、これは、メモリを解放するルーチンがIEで機能するかどうかを確認するのにも非常に役立ちます。


1

参照型は、オブジェクトが割り当てられている変数に直接オブジェクトを格納しないため、この例のオブジェクト変数には、実際にはオブジェクトインスタンスが含まれていません。代わりに、オブジェクトが存在するメモリ内の場所へのポインタ(または参照)を保持します

var object = new Object();

ある変数を別の変数に割り当てると、各変数はポインターのコピーを取得しますが、どちらもメモリー内の同じオブジェクトを参照します。

var object1 = new Object();
var object2 = object1;

1つのオブジェクトを指す2つの変数

JavaScriptはガベージコレクションされた言語であるため、参照型を使用するときにメモリ割り当てについて心配する必要はありません。ただし、不要になったオブジェクトを逆参照して、ガベージコレクターがそのメモリを解放できるようにすることをお勧めします。これを行う最良の方法は、オブジェクト変数をnullに設定することです。

var object1 = new Object();
// do something
object1 = null; // dereference

オブジェクトの逆参照は、何百万ものオブジェクトを使用する非常に大規模なアプリケーションでは特に重要です。

オブジェクト指向JavaScriptの原則から-NICHOLAS C. ZAKAS

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.