会社では、専門家が議論中にメモリリークについて話しているのをよく聞きますが、私は最近、主に Web 開発にエネルギーを費やしています (この本にはとても良い本があります)。タイトル) これはハッタリです。実際、著者は JavaScript のガベージ コレクション メカニズムを簡単なものから奥深いものまで学び、メモリ リークについてある程度の理解を深めました。
C# や Java と同様、JavaScript には自動ガベージ コレクション メカニズムがあり、コードの実行中に使用されるメモリの管理は実行環境が担当します。開発中にメモリの割り当てや不要なメモリのリサイクルを考慮する必要はありません。プロセス。 JavaScript ガベージ コレクションのメカニズムは非常に単純です。使用されなくなった変数を見つけて、その変数が占有しているメモリを解放します。ただし、このプロセスは高価であるため時間がかかりません。そのため、ガベージ コレクターは定期的にデータを収集します。一定間隔で実施します。
可変ライフサイクル
上記を読んだ後、「使用されなくなった変数とは何ですか?」と尋ねる生徒もいます。使用されなくなった変数は、ライフサイクルが終了した変数です。もちろん、グローバル変数のライフサイクルは、ブラウザーがページをアンロードするまで終了しません。ローカル変数は関数の実行中にのみ存在し、このプロセス中に、値を保存するためにスタックまたはヒープ上のローカル変数に対応するスペースが割り当てられ、その後、これらの変数は関数の終了まで関数内で使用されます。 (クロージャ 内部関数のため、外部関数は終わりとみなされません。クロージャを理解するには、JavaScript スコープ チェーンと JavaScript クロージャとは正確には何なのかを見てください。)
関数が終了すると、ローカル変数は必要なくなり、ローカル変数が占有していたメモリを解放できます。なぜ猫ととても単純な仕事に多額のお金がかかるのでしょうか?これはガベージ コレクションの氷山の一角にすぎません。関数は終了したように見えますが、実際には終了していません。その変数は役に立ちません。不要になった変数にマークを付けて、将来のリサイクルの準備をします。役に立たないとマークするには多くの戦略があり、一般的な方法は 2 つあります
マークとスイープ
これは、JavaScript で最も一般的なガベージ コレクション メソッドです。関数内で変数を宣言するなど、変数が実行環境に入るとき、ガベージ コレクターは変数が環境から出るときにそれを「環境に入った」とマークします。関数の実行が終了する ) は、「環境からの離脱」としてマークされます。マークを付ける方法については、特殊ビットを反転する、リストを維持するなど、さまざまな方法があります。重要なのは、環境に入る変数がどのような戦略を使用するかということです。いつでも呼び出される可能性があります。
ガベージ コレクターは、実行時にメモリに保存されているすべての変数をマークし、環境内の変数と環境内の変数によって参照される変数 (クロージャ) を削除します。これらが完了した後も、マークは引き続き存在します。環境内の変数がこれらの変数にアクセスできなくなり、ガベージ コレクターがこれらのマークされた変数が占有するスペースを占有するため、削除されます。
ほとんどのブラウザはガベージ コレクションにこの方法を使用しています。違いは、IE のバージョンが低いだけであり、やはり IE です。 。 。
参照カウント
メモリ リークは、IE の下位バージョンで頻繁に発生します。これは、多くの場合、ガベージ コレクションに参照カウントが使用されていることが原因です。参照カウントの戦略は、各値が使用される回数を追跡することです。変数が宣言され、その変数に参照型が割り当てられると、値への参照の数が 1 ずつ増加します。が別の に変更されると、この値への参照の数が 1 ずつ減ります。この値への参照の数が 0 になると、変数が使用されていないことを意味し、この値にアクセスできなくなります。ガベージ コレクターが実行されると、参照カウントが 0 の値が占めていた領域がクリアされるため、リサイクルされます。
これは良い方法のように見えますが、これを採用しているブラウザが少ないのはなぜですか? これはメモリ リークも引き起こすのでしょうか?主な理由は、この方法では循環参照の問題を解決できないためです。たとえば、オブジェクト A にはオブジェクト B を指す属性があり、オブジェクト B にもオブジェクト A を指す属性があるため、相互に参照します
このように、a と b の参照時間は両方とも 2 です。test() の実行が完了した後でも、両方のオブジェクトが環境から離れています。マークと明確な戦略の下では問題ありません。環境はクリアされますが、これら 2 つのオブジェクトの参照数は 2 のままで 0 にならないため、参照カウント戦略では機能しません。そのため、この関数が使用されている場合、それらによって占有されているスペースはクリアされません。複数回呼び出されると、リサイクルされるスペースがなくなり、メモリ リークが発生します。
IE では、JavaScript オブジェクトはマークとクリアによってガベージ コレクションされますが、BOM と DOM オブジェクトは参照カウントによってガベージ コレクションされます。つまり、BOM と DOM が関与している限り、循環参照の問題が発生します。上の例を見て、こんなつまらないことを誰がするだろうかと思った学生もいました。
このコードは問題ないように見えますが、obj は document.getElementById("element") を参照しており、document.getElementById("element") の onclick メソッドは、当然のことながら obj を含む外部環境の変数を参照します。とても隠されています。
解決策
最も簡単な方法は、手動で循環参照を自分で解放することです。たとえば、先ほどの関数は次のように実行できます
Microsoft は IE7 で調整を行いました。ガベージ コレクターによって回復されるメモリ割り当て量が占有メモリの 15% 未満の場合、トリガー条件は動的に変更されます。プログラムでは、メモリの大部分が再利用できないことを意味します。このとき、再利用されるメモリの割合が 85% を超えると、ガベージ コレクションのトリガー条件が 2 倍になります。メモリはずっと前にクリーンアップされているはずです。この時点で、トリガー条件を元に戻してください。これにより、ガベージ コレクション ジョブの機能が大幅に向上します。 C# や Java と同様に、ガベージ コレクション プログラムを手動で呼び出すこともできますが、大量のリソースを消費し、手動で呼び出す内容はブラウザが判断できるものよりも正確ではないため、呼び出すことはお勧めできません。ガベージ コレクション プログラムを手動で実行します。