弱参照

ごみ収集 (Web)

[52] HTMLDOM は様々なオブジェクトやそれに類するもので構成されています。 それらは文書 (Webアプリケーション) を構成する要素として存在していたり、 スクリプトによって操作されたりします。どこからも参照されなくなり、 スクリプト上も利用者界面上も意義を失ったオブジェクト等はごみ収集 (garbage collection) され、 破棄されます。それはすなわち Webブラウザー実行環境のメモリーから削除され、 動作もアクセスもできなくなることを意味します。

仕様書

ごみ収集アルゴリズム

[107] Webブラウザーごみ収集を実装しなければなりません。

[108] スクリプトをまったく実装しない Webブラウザーは、 一般的に「ごみ収集」と呼ばれるものを実装せず、単に必要なくなった文書解放するだけで済むかもしれません。

[109] 具体的な方法は規定されていません。実装は任意の方法を選択できます。

[42] 実際のところ、ごみ収集に関する規定はありますが、 ごみ収集を実装しなければならないという規定はどの仕様書にもありません。 しかしごみ収集しない場合、メモリー上の利用可能領域は徐々に減少し、 いずれ Webブラウザー自体が動作しなくなります。 動作しなくなった時点で Webブラウザー仕様書上の各規定に従うことができませんから、 (無限に大きなメモリーを有さない限り) ごみ収集しない Webブラウザー適合できません。

[110] 単なる参照カウンターでは、循環参照を持ち外部から参照されなくなったオブジェクト群を回収できません。 著者スクリプト循環参照を破棄することは求められていませんから、 メモリリークが発生してしまいます。仕様書アルゴリズム循環参照の破棄はしていませんから、 実装時には注意が必要です。

[111] メモリーリークを発生させること自体は仕様書の規定に不適合であるとは言い切れず、 実装の品質の問題に過ぎないかもしれませんが、メモリーリークによりメモリー領域の確保ができず動作に支障が出るなら、 仕様違反ということになりそうです。 (そもそも仕様に違反するかどうか以前に、プログラムの動作に対する一般的な期待として、 メモリーリーク不具合とみなされるとは思いますが...)

観測可能性

[84] WebJavaScript においてはごみ収集の具体的な手法は実装依存となっており、 その挙動は著者から観測可能であるべきではないと考えられています。

[85] 言語によってはオブジェクトが破棄される直前に特定のメソッドが呼び出されるなどの仕組みがありますが、 Web にはそれに類するものはありません。

[96] この原則に反する新機能の導入は避けるべきだと考えられており、 回避できないために断念された機能もあります。

  • [86] DOM3 には UserDataHandler節点の破棄の直前に呼び出す仕組みがありましたが、 削除されました。詳しくは UserDataHandler の項を参照してください。
  • [97] ポートの一端が閉じられたことを通知する仕組みが提案されましたが、 実装されることはありませんでした (>>81)。
  • [35] PortCollection は実装されませんでした。

[98] ごみ収集の詳細を著者 (スクリプト) から観測可能にしてしまうと、 Webブラウザーの実装の進化を阻害してしまう危険性があります。

[99] 例えばごみ収集には様々な手法が存在しますが、 特定のタイミングで特定の処理が実行されることを保証しようとすると、 それを実現可能なごみ収集手法の実装を強制することになり、 ごみ収集手法の改良を妨げてしまうおそれがあります。 逆に当該処理の詳細な実行タイミングを定義しないと、 ごみ収集その他の実装の差異によって、実装ごとに異なるタイミングで処理が実行され、 相互運用性の問題が生じる可能性があります。

[100] liveコレクションは、対象となるDOM木の変更が反映されますが、 実装上はリアルタイムで変更を反映させる必要はなく、 スクリプトからアクセスがあった時まで変更の反映を遅延させることもできます。 あるいは、節点のリストを途中までだけ作成したり、 リストの一部だけ変更を反映させた状態にしたりと、 色々な実装方針が考えられ、どれを採用するかは各実装の裁量です。

[101] さてDOM木から節点が削除され、スクリプトからも参照されなくなった時、 その節点はおそらくコレクションからも削除されることになりますが、 ごみ収集が実行可能になるタイミングは、コレクションの更新のタイミングに依存します。 ごみ収集が観測可能になると、コレクションの実装方法にも制約が生じます。

[102] 多くの Webブラウザーでは console.log により、あるいは開発者ツールにより JavaScript オブジェクトにアクセスできます。 つまり開発者ツールからオブジェクトへの参照が生じます。 スクリプト側での GC のタイミングに制約があるとすると、開発者ツール側で参照が残っていてもスクリプト側では開放したことにしなければならず、 より実装が複雑になるかもしれません。 (少なくても実装方法への制約は強くなると思われます。)

ごみ収集と参照

[51] HTMLDOM も、「ごみ収集 (ガーベージ・コレクション) (garbage collection) 」、 「強参照 (strong reference) 」、「弱参照 (weak reference) 」 といった用語を定義なしで使っており、計算機科学分野での一般的な単語として用いていると思われます。 (なお HTML は「弱参照」という用語を使っていません。)

弱参照

[80] JavaScript には WeakMapWeakSet があります。

[119] その他にいくつか仕様書上の概念が弱参照を使っています。

[70] 環境設定群オブジェクト未決拒絶された約束弱集合は、 そのメンバーに対して強参照を作ってはならない >>36 と規定されています。

暗示強参照

[3] 既存のオブジェクトを返すIDL属性からは、そのオブジェクトへの暗示強参照 (implied strong reference) があります。 >>2

[4] 例えば、 window.document IDL属性により、 Window オブジェクトから Document オブジェクト強い参照があることとなります。

[5] Document からその子孫節点へは (直接または間接の) 強い参照があります。

[112]節点から節点文書へは強い参照があります。

歴史

内部的な属性値への参照

[79] オブジェクトには、しばしば IDL属性として直接外部から参照可能でなくとも、 内部的な属性のようなものとして別のオブジェクトが関連付けられていることがあります。 これも強参照が存在し、最初のオブジェクトが存在する限り関連付けられているオブジェクトも破棄されないものと思われます。

循環参照

[87] オブジェクト同士の参照は、直接または間接に循環していることがあります。例えば、 親節点から子節点へは childNodes を通じて参照がありますし、 子節点から親節点へは parentNode を通じて参照があり、循環しています。 また JavaScript によって著者が容易に循環参照を作り出すことができます。

[88] 従って Web におけるごみ収集は、外部から参照されなくなった循環参照のあるオブジェクト群を回収できる手法を用いる必要があります。

ごみ収集による動作の停止

[93] ごみ収集の実装手法の中には、ごみ収集の処理のために本来のプログラムの動作をすべて停止する必要があるものもあります。

[94] 明示的な規定は存在していないものの、利用者に明確に判別可能なレベルで動作を停止させるような実装方法は、 好ましくないと考えられます。

[95] Acid3 はそのアニメーションが滑らかであることを要求しており、 この試験に通過するためには、実行途中でごみ収集によって速度が変化するような実装であってはいけません。

[40] ただしこれは実装の品質の問題と考える人もいます。

閲覧文脈

[13] 利用者エージェント最上位閲覧文脈に対して強参照を持っています。 >>11

[12] 閲覧文脈は、その Document 群とその WindowProxy に対して強参照を持っています。 >>11

[17] 閲覧文脈から Document への参照Documentを捨てる算法で削除されます。

[14] Document はその Window に対して強参照を持っています。逆に Windowdocument 属性を通じて暗示強参照を持っています。 >>11

[15] Windowwindow など、 DocumentdefaultView などを通じて WindowProxy への暗示強参照を持っています。 >>11

[16] スクリプトスクリプト設定群オブジェクトへの強い参照を持っており、 スクリプト設定群オブジェクト大域オブジェクト有責閲覧文脈有責文書に対して強参照を持っています。 >>11

[19] 最上位閲覧文脈でない閲覧文脈WindowProxyごみ収集の対象になると、 当該閲覧文脈閲覧文脈を捨てる算法により処理されなければなりません。 >>11

歴史

変異観察器

[45] 節点は、登録済み観察器のリストにある登録済み観察器に対する強参照を持ちます。 逆方向には弱参照を持ちます。 >>44

ワーカー

[21] DedicatedWorkerGlobalScope/Worker は暗示的に関連付けられた MessagePort オブジェクトを有しますが、この MessagePortDedicatedWorkerGlobalScope/Worker より先にごみ収集してはなりません>>20, >>24

[23] ワーカー実行時誤りerror 事象として呼び出し元の WorkerDocument へと伝達されていきますが、その伝達先が既にごみ収集されて消失している場合には onerrornull であるものとして (つまり何もしないで更に上位の呼び出し元へと伝達されるよう) 処理されます。 >>22

[90] ワーカースクリプト設定群オブジェクトを介してワーカーを起動した大域オブジェクトDocument への参照を保有しています。

MessagePort

[39] MessagePort についてはオブジェクト自身が著者スクリプトから参照されなくなっても、 相手方が存在する限り存続することがあります。

MessagePort 参照。

[81] MessagePort の一端が失われた場合に他方に通知する方法も検討されたものの、 ごみ収集の挙動をスクリプトから観測できず、メモリーリークもさせず、 bfcache も実装可能であるような手法が見つからず、議論がまとまりませんでした。

[39] PortCollectionMessagePort を零個以上保有することができるオブジェクトですが、強参照ではないので MessagePortごみ収集を防ぐ役割は持たず、 ごみ収集された MessagePort は削除されていきます。 しかしこれもごみ収集の実装の自由を損なうとして、実装されることはありませんでした。

BroadcastChannel

[38] BroadcastChannel 参照。

画像

[47] 画像データの更新 (update the image data) 算法の実行中は、 その対象となっている img 要素文書から当該要素への強参照があるとしなければなりません>>46

[48] Documentにある要素なら自然に満たされますが、 そうではなく単独で存在する img 要素であっても、 画像の読み込みが終わるまでは破棄されないことになります。

[50] CanvasProxy無効でなければ対応する画布要素への強参照があるとしなければなりません>>49

ヒット領域

[83] canvasヒット領域は、削除されたり消去されて対応する画素がなくなり、 他のヒット領域子供でもないなら、領域をごみ収集する (garbage-collect the regions) 手順により削除されます >>82

媒体要素

[7] 媒体要素は、それに対するすべての参照が削除されただけの理由で再生 (playing) を停止してはなりません。 それ以上音声 (audio) を再生できない状態になった時、当該媒体要素ごみ収集して構いません>>6

[8] 要素に対して明示的に参照が存在しておらず、まだ活性的に再生 (actively playing) されていなくとも、 音声再生のために存在し続けることがあります。例えばまだ参照されている現在媒体制御器があって再生再開 (unpause) できるかもしれませんし、 あるいは再生再開されていてもバッファ待ちのため stall 中かもしれません。 >>6

[10] 環境によっては同時再生数の制限が厳しいかもしれませんから、 再生が終わったらすべての参照を確実に削除するなど配慮することが特に好ましいとされています。 >>9

Blob

WebRTC

XHR

[54] XMLHttpRequest オブジェクトは次の場合ごみ収集してはなりません>>53

[62] XMLHttpRequest オブジェクト接続が開いたままごみ収集される場合、 fetch をすべて取り消し、それらにより追加されたタスクをすべて破棄し、 またそれらによりネットワークから受信しているデータがあれば破棄しなければなりません>>61

事象源

[26] EventSource については、

... には、 EventSource構築子が呼び出された Window または WorkerUtils から EventSource への強参照がなければなりません>>26

[30] EventSource接続が開いている間に同オブジェクトがごみ収集される場合、当該オブジェクトによって開かれた fetch は中断しなければなりません>>26

歴史

Web Sockets

[32] WebSocket オブジェクトは、 イベントリスナーバッファーの状況次第で、ごみ収集してはなりません >>31

[37] また WebSocket オブジェクト接続が開いたままごみ収集される場合は、 接続を閉じる手順を実行しなければなりません >>31

Notification

[92] Notification は、対応する通知list of pending notificationslist of active notifications に含まれていて、 click, show, error, close のいずれかのイベントリスナーを持つ間は、これをごみ収集してはいけません >>91

歴史

[1] Web におけるごみ収集 (garbage collection) は HTML Living Standard ではじめて明示的に規定され、 その後他の仕様でも定義されるようになりました。

メモ

[73] オブジェクト著者がアクセスできる範囲の外から参照されることもあり得ます。例えば console.log メソッドオブジェクトを渡すと、 その環境のデバッガーが当該オブジェクトへの参照を保持し、 スクリプト内から参照しなくなったとしてもごみ収集されずに残り続けることがあります。

[75] Incremental GC in Firefox 16! | JavaScript ( ( 版)) <https://blog.mozilla.org/javascript/2012/08/28/incremental-gc-in-firefox-16/>

[76] JavaScript:SpiderMonkey:GC Futures - MozillaWiki ( ( 版)) <https://wiki.mozilla.org/JavaScript:SpiderMonkey:GC_Futures>

[77] substructural: Programming, Entrepreneurship, and the Markets: Garbage Collection in JavaScript, Part 1 ( ( 版)) <http://www.typedynamic.com/2012/03/garbage-collection-in-javascript.html>

[78] Web Applications 1.0 r2529 MAJOR CHANGES: Revamp the way scripts are specified, along with their interaction with resolving relative URLs, etc. Give enough detail to justify objects in the DOM not being garbage collected randomly when still in use. Define script groups, to handle scripts going away during document.open() and session history navigation. Define why and how setTimeout(), database transactions, etc, handle page transitions. Drop the terms 'with' and 'without' script, use script is 'enabled'/'disabled' instead. Define 'unload' and 'beforeunload'. Rework how onfoo= and .onfoo event handler attributes are defined. Rework how the content model of <noscript> is defined. Reword the way javascript: is defined to use the new terminology. Add a few notes of things that came up while I was doing all that.]] ( ( 版)) <http://html5.org/tools/web-apps-tracker?from=2528&to=2529>

[89] エデンの園でおきたこと - steps to phantasien ( (Hajime Morrita 著, 版)) <http://steps.dodgson.org/b/2012/12/19/dom-and-gc-or-what-happend-at-eden/>

[103] Bug 25223 – IDB exposes GC behavior ( ( 版)) <https://www.w3.org/Bugs/Public/show_bug.cgi?id=25223>

[104] Bug 26515 – Change when media elements may be garbage collected ( ( 版)) <https://www.w3.org/Bugs/Public/show_bug.cgi?id=26515>

[105] Specify life time management · Issue #15 · yutakahirano/fetch-with-streams ( 版) <https://github.com/yutakahirano/fetch-with-streams/issues/15#issuecomment-72119046>

[33] Feature suggestion - event from the JS Garbage Collector · Issue #238 · WebAssembly/design ( 版) <https://github.com/WebAssembly/design/issues/238>

[34] Add description for suspension, resuming and garbage collection · whatwg/fetch@1dfbd1f ( 版) <https://github.com/whatwg/fetch/commit/1dfbd1f6284e4cb0aae91a8b9db36a2dc11393b1>

[114] Move structured cloning out from under Common DOM Interfaces · whatwg/html@e09b782 ( 版) <https://github.com/whatwg/html/commit/e09b7824480ffc9df69b0d830c4b8adad3b000d1>

[115] Clarify settings object, realm, and global relationships · whatwg/html@0866f1b ( 版) <https://github.com/whatwg/html/commit/0866f1b3f4b4ea5a99a30909e9bbe557dea0b460>

[116] Custom elements disconnected from a document should not be upgraded · Issue #419 · w3c/webcomponents ( ()) <https://github.com/w3c/webcomponents/issues/419>

[117] Workers: Curtail the browser's license to kill · Issue #1004 · whatwg/html () <https://github.com/whatwg/html/issues/1004>

[41] GC / DOM / Web API Integration :unicorn: - WebAssembly () <http://webassembly.org/docs/gc/>

[43] Abortable fetch (jakearchibald著, ) <https://github.com/whatwg/fetch/commit/0bcd5dfc71ef44319873887f4b83119bd6d0b71d>

[121] Sensor must not expose GC behavior (pozdnyakov著, ) <https://github.com/w3c/sensors/commit/9fd7eeb7a69635ea9e3b0f4eb0402c677bba19e5>

[122] Must not expose GC behavior to the web · Issue #337 · w3c/sensors () <https://github.com/w3c/sensors/issues/337>

[123] Sensor must not expose GC behavior by pozdnyakov · Pull Request #338 · w3c/sensors () <https://github.com/w3c/sensors/pull/338>

[124] Permit freeing underlying source / sink / transformer after close · Issue #932 · whatwg/streams () <https://github.com/whatwg/streams/issues/932>