この記事では、主に js のガベージ コレクションのメカニズムを紹介します。これは、必要な友人に参考にしていただけるようになりました。
必要なくなったらどうなりますか? JavaScript エンジンはどのようにしてそれらを見つけてクリアするのでしょうか?
到達可能性
です。 簡単に言うと、「到達可能な」値はアクセス可能で利用可能であり、メモリに安全に保存されます。
这些值都称为 *roots*。
ローカル変数にオブジェクトが存在し、その値が別のオブジェクトを参照しているとします。このオブジェクトが参照できる場合、そのオブジェクトが参照するオブジェクトも参照できます。詳細な例は後で説明します。
簡単な例
// user has a reference to the object let user = { name: "John" };
矢印はオブジェクト参照を表します。グローバル変数 "user"
は、オブジェクト {name: "John"}
(John と呼ばれます) を参照します。 John の "name"
属性にはプリミティブ値が格納されるため、他に参照はありません。
user
を上書きすると、John への参照が失われます: "user"
引用了对象{name: "John"}
(简称此对象为 John)。John 的 "name"
属性储存的是一个原始值,所以无其他引用。
如果覆盖 user
,对 John 的引用就丢失了:
user = null;
现在 John 变得不可触及,垃圾回收机制会将其删除并释放内存。
如果我们从 user
复制引用到 admin
:
// user has a reference to the object let user = { name: "John" }; *!* let admin = user; */!*
如果重复一次这个操作:
user = null;
……这个对象是依然可以通过 admin
访问的,所以它依然存在于内存。如果我们把 admin
也覆盖为 null,那它就会被删除了。
这个例子比较复杂:
function marry(man, woman) { woman.husband = man; man.wife = woman; return { father: man, mother: woman } } let family = marry({ name: "John" }, { name: "Ann" });
marry
函数让两个参数对象互相引用,返回一个包含两者的新对象,结构如下:
暂时所有对象都是可触及的,但我们现在决定移除两个引用:
delete family.father; delete family.mother.husband;
只删除一个引用不会有什么影响,但是两个引用同时删除,我们可以看到 John 已经不被任何对象引用了:
即使 John 还在引用别人,但是他不被别人引用,所以 John 现在已经是不可触及的了,它的存在将会被移除。
垃圾回收后:
也可能有一大堆互相引用的对象整块(像个孤岛)都不可触及了。
对上面的对象进行操作:
family = null;
内存中的情况如下:
这个例子展示了“可触及”这个概念的重要性。
尽管 John 和 Ann 互相依赖,但这仍不足够。
"family"
rrreee
user
から admin
に参照をコピーすると: 🎜rrreee🎜🎜🎜この操作を繰り返すと 🎜rrreee🎜...これオブジェクトはい、admin
を通じて引き続きアクセスできるため、メモリ内にまだ存在します。 admin
も null に上書きすると、削除されます。 🎜🎜相互参照するオブジェクト🎜🎜 この例はより複雑です: 🎜rrreee🎜marry
この関数は 2 つのパラメーター オブジェクトを相互参照させ、両方を含む新しいオブジェクトを返します。構造は次のとおりです。 🎜🎜🎜 現時点ではすべてのオブジェクトに到達可能ですが、次の 2 つの参照を削除することにしました: 🎜rrreee🎜🎜🎜 1 つの参照だけを削除しても影響はありませんが、両方の参照を同時に削除すると、次のことがわかります。 John はもういません 参照されているオブジェクト: 🎜🎜🎜🎜ジョンは依然として他者を参照していますが、他者によって引用されていないため、ジョンはアンタッチャブルとなり、彼の存在は削除されます。 🎜🎜ガベージコレクション後: 🎜🎜🎜🎜到達不能な島🎜🎜 相互に参照するオブジェクトが多数あり、ブロック全体 (島など) が到達不能になる可能性もあります。 🎜🎜上記のオブジェクトに対する操作: 🎜rrreee🎜 メモリ内の状況は次のとおりです: 🎜🎜🎜🎜この例は、「到達可能性」の概念の重要性を示しています。 🎜🎜ジョンとアンはお互いを頼りにしていましたが、それだけでは十分ではありませんでした。 🎜🎜"family"
オブジェクト全体がルート接続から切り離されており、何も参照していないため、この島は手の届かないところにあり、削除されるのを待つしかありません。 🎜基本的なガベージ コレクション アルゴリズムは、「マーク アンド スイープ」と呼ばれます:
ガベージ コレクターはルートを取得してマークします。
次に、それらからのすべての引用にアクセスしてタグを付けます。
マークされたオブジェクトにアクセスするには、それらの 参照をマークします。訪問したオブジェクトはすべて記録され、今後同じオブジェクトが繰り返しアクセスされることはありません。
…未訪問の参照だけが残るまで。
マークされていないオブジェクトはすべて削除されます。
オブジェクトの構造が次のとおりであると仮定します。
右側に「島」がはっきりと見えます。ここで、「マークしてクリア」方法を使用してこれに対処します。
最初のステップ、ルートをマークします:
次に、それらの参照をマークします:
... 参照されている参照をマークします:
これで、アクセスされていないオブジェクトはアクセスできないとみなされます。削除されます:
これがガベージ コレクションの仕組みです。
JavaScript エンジンは、実行に影響を与えることなく多くの最適化を行い、このプロセスのガベージ コレクションの効率を高めました:
世代コレクション -- オブジェクトは「新しい世代」と「古い世代」に分割されます。多くのオブジェクトはタスクが完了するとすぐに不要になるため、頻繁にクリーンアップできます。除染中に取り残された人々は「旧世代」と呼ばれる。
増分コレクション -- オブジェクトが多数ある場合、すべてのオブジェクトを一度にマークすることは難しく、このプロセスによりプログラムの実行に大幅な遅延が発生することもあります。したがって、エンジンはこの操作を複数の部分に分割し、一度に 1 つの部分を実行しようとします。これを行うには追加のデータを記録する必要がありますが、ユーザー エクスペリエンスに対する遅延の影響を効果的に軽減できます。
アイドル時間収集 -- ガベージ コレクターは、プログラムの実行への影響を軽減するために、CPU がアイドル状態のときにのみ実行しようとします。
さらに、ガベージ コレクションには多くの最適化がありますが、ここでは詳しく説明しません。各エンジンには独自の調整とテクノロジがあり、これは実用的でない場合は常に変更されます。ニーズをあまり深く掘り下げる価値はありません。ただし、これに本当に興味がある場合は、いくつかの拡張リンクが以下に提供されます。
重要なポイント:
ガベージコレクションは自動的に行われ、強制したり停止したりすることはできません。
到達可能なオブジェクトはメモリに保持されます。
参照は必ずしも (ルートから) 到達可能であるとは限りません。相互に参照するオブジェクトは完全に到達不能になる可能性があります。
最新のエンジンは強化されたガベージ コレクション アルゴリズムを実装しており、それらについては書籍『ガベージ コレクション ハンドブック: 自動メモリ管理の技術』(R. Jones et al.) で言及されています。
低レベル プログラミングに精通している場合は、「V8 のツアー: ガベージ コレクション」を読んで、V8 ガベージ コレクションの詳細を学ぶことができます。
V8 ブログでもメモリ管理に関する記事を頻繁に公開しています。ガベージ コレクション アルゴリズムを学ぶ最良の方法は、まず V8 実装を学び、Vyacheslav Egorov (V8 エンジニアの 1 人) のブログを読むことです。 V8 と言ったのは、インターネット上に V8 に関する記事がたくさんあるからです。他のエンジンの場合、多くの実装は類似していますが、ガベージ コレクション アルゴリズムには多くの違いがあります。
エンジンを深く理解することは、低レベルの最適化を行うときに非常に役立ちます。言語に慣れてきたら、これを追求するのが賢明な方向です。
上記がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。
関連する推奨事項:
ページがPCで開かれたかモバイルで開かれたかをJSで判断する方法
以上がjsガベージコレクションの内容について簡単に説明しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。