[52] [[HTML]] や [[DOM]] は様々な[[オブジェクト]]やそれに類するもので構成されています。 それらは[[文書]] ([[Webアプリケーション]]) を構成する要素として存在していたり、 [[スクリプト]]によって操作されたりします。どこからも[[参照]]されなくなり、 [[スクリプト]]上も[[利用者界面]]上も意義を失った[[オブジェクト]]等は[DFN[[RUBYB[ごみ収集]@en[garbage collection]]]]され、 破棄されます。それはすなわち [[Webブラウザー]]実行環境の[[メモリー]]から削除され、 動作もアクセスもできなくなることを意味します。 * 仕様書 [REFS[ - [2] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [6] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [9] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [11] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [20] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [24] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [22] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [25] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [31] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [38] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [44] [CITE@en-US[DOM Standard]] ([TIME[2012-11-16 19:17:11 +09:00]] 版) - [46] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [49] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2012-11-16 20:26:18 +09:00]] 版) - [53] [CITE@en-US[XMLHttpRequest Standard]] ([TIME[2012-11-14 13:48:01 +09:00]] 版) - [82] [CITE@en-US-x-hixie[HTML Standard]] ([TIME[2014-03-07 22:55:24 +09:00]] 版) ]REFS] * 観測可能性 [84] [[Web]] と [[JavaScript]] においては[[ごみ収集]]の具体的な手法は[[実装依存]]となっており、 その挙動は[[著者]]から[[観測可能]]であるべきではないと考えられています。 [85] [[言語]]によっては[[オブジェクト]]が破棄される直前に特定の[[メソッド]]が呼び出されるなどの仕組みがありますが、 [[Web]] にはそれに類するものはありません。 [86] [[DOM3]] には [CODE(DOMi)@en[[[UserDataHandler]]]] を[[節点]]の破棄の直前に呼び出す仕組みがありましたが、 削除されました。詳しくは [CODE(DOMi)@en[[[UserDataHandler]]]] の項を参照してください。 * ごみ収集と参照 [51] [[HTML]] も [[DOM]] も、「[DFN[[RUBY[ごみ収集][ガーベージ・コレクション]@en[garbage collection]]]]」、 「[DFN[[RUBYB[強参照]@en[strong reference]]]]」、「[DFN[[RUBYB[弱参照]@en[weak reference]]]]」 といった用語を定義なしで使っており、[[計算機科学]]分野での一般的な単語として用いていると思われます。 [WEAK[(なお [[HTML]] は「[[弱参照]]」という用語を使っていません。)]] * 暗示強参照 [3] 既存の[[オブジェクト]]を返す[[IDL属性]]からは、その[[オブジェクト]]への[DFN[[RUBYB[[[暗示強参照]]]@en[implied strong reference]]]]があります。 [SRC[>>2]] [EG[ [4] 例えば [CODE(JS)@en[[[document]].[[location]]]] [[属性]]は [CODE(DOMi)@en[[[Location]]]] [[オブジェクト]]を持っているので、これはすなわち、当該 [CODE(DOMi)@en[[[Document]]]] [[オブジェクト]] [WEAK[(の [CODE(DOMa)@en[[[location]]]] [[属性]])]] から [CODE(DOMi)@en[[[Location]]]] [[オブジェクト]]への[[強参照]]があることになります。 ]EG] [EG[ [5] [CODE(DOMi)@en[[[Document]]]] からその[[子孫節点]]へは [WEAK[([CODE(DOMa)@en[[[childNodes]]]] [[属性]]などを通じて)]] [[強参照]]があり、また各[[節点]]から [CODE(DOMi)@en[[[Document]]]] へは [WEAK[([CODE(DOMa)@en[[[ownerDocument]]]] [[属性]]などを通じて)]] [[強参照]]があります。 ]EG] ** 歴史 [REFS[ - [67] [CITE@en[Web Applications 1.0 r2529 MAJOR CHANGES: ...]] ([TIME[2008-12-12 17:46:00 +09:00]] 版) ]REFS] * 内部的な属性値への参照 [79] [[オブジェクト]]には、しばしば [[IDL属性]]として直接外部から参照可能でなくとも、 内部的な属性のようなものとして別の[[オブジェクト]]が関連付けられていることがあります。 これも[[強参照]]が存在し、最初の[[オブジェクト]]が存在する限り関連付けられている[[オブジェクト]]も破棄されないものと思われます。 * 循環参照 [87] [[オブジェクト]]同士の参照は、直接または間接に循環していることがあります。例えば、 [[親節点]]から[[子節点]]へは [CODE(DOMa)@en[[[childNodes]]]] を通じて参照がありますし、 [[子節点]]から[[親節点]]へは [CODE(DOMa)@en[[[parentNode]]]] を通じて参照があり、循環しています。 また [[JavaScript]] によって[[著者]]が容易に[[循環参照]]を作り出すことができます。 [88] 従って [[Web]] における[[ごみ収集]]は、外部から参照されなくなった[[循環参照]]のある[[オブジェクト]]群を回収できる手法を用いる必要があります。 * 閲覧文脈 [13] [[利用者エージェント]]は[[最上位閲覧文脈]]に対して[[強参照]]を持っています。 [SRC[>>11]] ;; [18] [[利用者エージェント]]から[[閲覧文脈]]への[[強参照]]は[[閲覧文脈を捨てる]][[算法]]で削除されます。 [12] [[閲覧文脈]]は、その [CODE(DOMi)@en[[[Document]]]] 群とその [CODE(DOMi)@en[[[WindowProxy]]]] に対して[[強参照]]を持っています。 [SRC[>>11]] ;; [17] [[閲覧文脈]]から [CODE(DOMi)@en[[[Document]]]] への[[参照]]は[[[CODE(DOMi)@en[Document]]を捨てる]][[算法]]で削除されます。 [14] [CODE(DOMi)@en[[[Document]]]] はその [CODE(DOMi)@en[[[Window]]]] に対して[[強参照]]を持っています。逆に [CODE(DOMi)@en[[[Window]]]] は [CODE(DOMa)@en[[[document]]]] [[属性]]を通じて[[暗示強参照]]を持っています。 [SRC[>>11]] [15] [CODE(DOMi)@en[[[Window]]]] は [CODE(DOMi)@en[[[window]]]] など、 [CODE(DOMi)@en[[[Document]]]] は [CODE(DOMi)@en[[[defaultView]]]] などを通じて [CODE(DOMi)@en[[[WindowProxy]]]] への[[暗示強参照]]を持っています。 [SRC[>>11]] [16] [[スクリプト]]は[[スクリプト設定群オブジェクト]]への[[強い参照]]を持っており、 [[スクリプト設定群オブジェクト]]は[[大域オブジェクト]]、[[有責閲覧文脈]]、 [[有責文書]]に対して[[強参照]]を持っています。 [SRC[>>11]] [19] [[最上位閲覧文脈]]でない[[閲覧文脈]]の [CODE(DOMi)@en[[[WindowProxy]]]] が[[ごみ収集]]の対象になると、 当該[[閲覧文脈]]は[[閲覧文脈を捨てる]][[算法]]により処理されなければなりません。 [SRC[>>11]] ** 歴史 [REFS[ - [68] [CITE@en[Web Applications 1.0 r2529 MAJOR CHANGES ...]] ([TIME[2008-12-12 17:46:00 +09:00]] 版) ]REFS] * 変異観察器 [45] [[節点]]は、[[登録済み観察器]]のリストにある[[登録済み観察器]]に対する[[強参照]]を持ちます。 逆方向には[[弱参照]]を持ちます。 [SRC[>>44]] * ワーカー [21] [CODE(DOMi)@en[[[DedicatedWorkerGlobalScope]]]]/[CODE(DOMi)@en[[[Worker]]]] は暗示的に関連付けられた [CODE(DOMi)@en[[[MessagePort]]]] [[オブジェクト]]を有しますが、この [CODE(DOMi)@en[[[MessagePort]]]] は [CODE(DOMi)@en[[[DedicatedWorkerGlobalScope]]]]/[CODE(DOMi)@en[[[Worker]]]] より先に[[ごみ収集]]しては[['''なりません''']]。 [SRC[>>20, >>24]] [23] [[ワーカー]]の[[実行時誤り]]は [CODE(DOMe)@en[[[error]]]] [[事象]]として呼び出し元の [CODE(DOMi)@en[[[Worker]]]] や [CODE(DOMi)@en[[[Document]]]] へと伝達されていきますが、その伝達先が既に[[ごみ収集]]されて消失している場合には [CODE(DOMa)@en[[[onerror]]]] は [CODE@en[[[null]]]] であるものとして [WEAK[(つまり何もしないで更に上位の呼び出し元へと伝達されるよう)]] 処理されます。 [SRC[>>22]] [90] [[ワーカー]]は[[スクリプト設定群オブジェクト]]を介して[[ワーカー]]を起動した[[大域オブジェクト]]や [CODE(DOMi)@en[[[Document]]]] への参照を保有しています。 * [CODE(DOMi)@en[MessagePort]] [41] [CODE(DOMi)@en[[[MessagePort]]]] が [[entangle]] されると、 その [CODE(DOMi)@en[[[MessagePort]]]] と [[entangle]] された [CODE(DOMi)@en[[[MessagePort]]]] が最初の [CODE(DOMi)@en[[[MessagePort]]]] への[[強参照]]を持つか、 または最初の [CODE(DOMi)@en[[[MessagePort]]]] の[[所有者]]が 最初の [CODE(DOMi)@en[[[MessagePort]]]] への[[強参照]]を持つかのいずれかであるかのように扱われなければ[['''なりません''']]。 従って [CODE(DOMi)@en[[[MessagePort]]]] を受け取って[[事象聴取器]]を登録してからその [CODE(DOMi)@en[[[MessagePort]]]] への明示的な参照を捨てたとしても、 その[[事象聴取器]]が[[メッセージ]]を受信できる限りにおいて[[通信路]]は維持されることになります。 もちろん[[通信路]]の両端で同様に参照が破棄された場合にはいずれの側も[[アプリケーション]]から到達できなくなりますから、 両 [CODE(DOMi)@en[[[MessagePort]]]] を[[ごみ収集]]することができます。 [SRC[>>38]] [42] [CODE(DOMi)@en[[[MessagePort]]]] は、当該[[オブジェクト]]で [[dispatch]] されることになる[[メッセージ]]が[[タスク源]]に存在する間、 あるいは[[ポート・メッセージ・キュー]]が開いていて [CODE(DOMe)@en[[[message]]]] [[事象]]が存在している場合には、[[ごみ収集]]しては[['''なりません''']]。 [SRC[>>38]] [81] [CODE(DOMi)@en[[[MessagePort]]]] の一端が失われた場合に他方に通知する方法も検討されたものの、 [[ごみ収集]]の挙動を[[スクリプト]]から観測できず、[[メモリーリーク]]もさせず、 [[bfcache]] も実装可能であるような手法が見つからず、議論がまとまりませんでした。 [39] [CODE(DOMi)@en[[[PortCollection]]]] は [CODE(DOMi)@en[[[MessagePort]]]] を零個以上保有することができる[[オブジェクト]]ですが、[[強参照]]ではないので [CODE(DOMi)@en[[[MessagePort]]]] の[[ごみ収集]]を防ぐ役割は持たず、 [[ごみ収集]]された [CODE(DOMi)@en[[[MessagePort]]]] は削除されていきます。 [SRC[>>38]] ;; [80] [CODE(DOMi)@en[[[PortCollection]]]] は実装されない可能性があります。 [43] [CODE(DOMi)@en[[[MessagePort]]]] が不要になったら、明示的に閉じて [[disentangle]] することが[RUBYB[強く勧められています]@en[strongly encouraged]]。 明示的に閉じずないと必ずしも即座に[[ごみ収集]]されるとは限らないため、 [[メモリー]]を無駄にすることとなります。 [SRC[>>38]] ** 歴史 [REFS[ - [66] [CITE@en[Web Applications 1.0 r2357 Define MessagePort such that they won't be garbage collected while a message is outstanding. (credit: ap)]] ([TIME[2008-10-21 04:18:00 +09:00]] 版) - [65] [CITE@en[Web Applications 1.0 r2024 Simplify garbage collection for ports even further. Define dicarding of Document objects better for ports. Prevent inactive documents from receiving messages.]] ([TIME[2008-08-06 16:57:00 +09:00]] 版) ]REFS] * 画像 [47] [RUBYB[[[画像データの更新]]]@en[update the image data]][[算法]]の実行中は、 その対象となっている [CODE(HTMLe)@en[[[img]]]] [[要素]]の[[文書]]から当該[[要素]]への[[強参照]]があるとしなければ[['''なりません''']]。 [SRC[>>46]] ;; [48] [[[CODE(DOMi)@en[Document]]中]]にある[[要素]]なら自然に満たされますが、 そうではなく単独で存在する [CODE(HTMLe)@en[[[img]]]] [[要素]]であっても、 [[画像]]の読み込みが終わるまでは破棄されないことになります。 [50] [CODE(DOMi)@en[[[CanvasProxy]]]] は[[無効]]でなければ対応する[[画布要素]]への[[強参照]]があるとしなければ[['''なりません''']]。 [SRC[>>49]] * ヒット領域 [83] [CODE(HTMLe)@en[[[canvas]]]] の[[ヒット領域]]は、削除されたり消去されて対応する[[画素]]がなくなり、 他の[[ヒット領域]]の[[子供]]でもないなら、[DFN[[RUBYB[領域をごみ収集する]@en[garbage-collect the regions]]]]手順により削除されます [SRC[>>82]]。 * 媒体要素 [7] [[媒体要素]]は、それに対するすべての[[参照]]が削除されただけの理由で[RUBYB[[[再生]]]@en[playing]]を停止しては[['''なりません''']]。 それ以上[RUBYB[[[音声]]]@en[audio]]を再生できない状態になった時、当該[[媒体要素]]は[[ごみ収集]]して[['''構いません''']]。 [SRC[>>6]] [8] [[要素]]に対して明示的に[[参照]]が存在しておらず、まだ[RUBYB[活性的に再生]@en[actively playing]]されていなくとも、 [[音声]]の[[再生]]のために存在し続けることがあります。例えばまだ[[参照]]されている[[現在媒体制御器]]があって[RUBYB[再生再開]@en[unpause]]できるかもしれませんし、 あるいは再生再開されていても[[バッファ]]待ちのため [[stall]] 中かもしれません。 [SRC[>>6]] ;; [10] 環境によっては同時再生数の制限が厳しいかもしれませんから、 再生が終わったらすべての[[参照]]を確実に削除するなど配慮することが特に好ましいとされています。 [SRC[>>9]] * Blob @@ [74] [[revokeObjectURL]] * WebRTC @@ [64] [CITE@en[WebRTC 1.0: Real-time Communication Between Browsers]] ([TIME[2012-11-15 19:30:32 +09:00]] 版) @@ [63] [CITE@en-us[Media Capture and Streams]] ([TIME[2012-11-15 19:30:32 +09:00]] 版) * XHR [54] [CODE(DOMi)@en[[[XMLHttpRequest]]]] [[オブジェクト]]は次の場合[[ごみ収集]]しては[['''なりません''']]。 [SRC[>>53]] - [55] 次のいずれかが成立し、 -- [56] 状態が [CODE(DOM)@en[[[OPENED]]]] であり [[[CODE(DOMm)@en[send()]]フラグ]]が設定されている -- [57] 状態が [CODE(DOM)@en[[[HEADERS_RECEIVED]]]] -- [58] 状態が [CODE(DOM)@en[[[LOADING]]]] - [59] かつ次のいずれかが成立する場合 -- [60] [CODE(DOMe)@en[[[readystatechange]]]], [CODE(DOMe)@en[[[progress]]]], [CODE(DOMe)@en[[[abort]]]], [CODE(DOMe)@en[[[error]]]], [CODE(DOMe)@en[[[load]]]], [CODE(DOMe)@en[[[timeout]]]], [CODE(DOMe)@en[[[loadend]]]] のいずれかの[[事象聴取器]]が登録されている場合 -- [61] [[アップロード完了フラグ]]が設定されておらず関連付けられた [CODE(DOMi)@en[[[XMLHttpRequestUpload]]]] [[オブジェクト]]が [CODE(DOMe)@en[[[progress]]]], [CODE(DOMe)@en[[[abort]]]], [CODE(DOMe)@en[[[error]]]], [CODE(DOMe)@en[[[load]]]], [CODE(DOMe)@en[[[timeout]]]], [CODE(DOMe)@en[[[loadend]]]] のいずれかの[[事象聴取器]]が登録されている場合 [62] [CODE(DOMi)@en[[[XMLHttpRequest]]]] [[オブジェクト]]の[[接続]]が開いたまま[[ごみ収集]]される場合、 [[fetch]] をすべて取り消し、それらにより追加された[[タスク]]をすべて破棄し、 またそれらによりネットワークから受信しているデータがあれば破棄しなければ[['''なりません''']]。 [SRC[>>61]] * 事象源 [26] [CODE(DOMi)@en[[[EventSource]]]] については、 - [28] [CODE(DOMa)@en[[[readyState]]]] が [CODE[[[CONNECTING]]]] であり、かつ [CODE(DOMe)@en[[[open]]]], [CODE(DOMa)@en[[[message]]]], [CODE(DOMa)@en[[[error]]]] のいずれかの[[事象聴取器]]が登録されている場合 - [27] [CODE(DOMi)@en[[[EventSource]]]] の [CODE(DOMa)@en[[[readyState]]]] が [CODE[[[OPEN]]]] であり、かつ [CODE(DOMa)@en[[[message]]]], [CODE(DOMa)@en[[[error]]]] のいずれかの[[事象聴取器]]が登録されている場合 - [29] [[遠隔事象タスク源]]に[[タスク]]が投入されている間 ... には、 [CODE(DOMi)@en[[[EventSource]]]] の[[構築子]]が呼び出された [CODE(DOMi)@en[[[Window]]]] または [CODE(DOMi)@en[[[WorkerUtils]]]] から [CODE(DOMi)@en[[[EventSource]]]] への[[強参照]]がなければ[['''なりません''']]。 [SRC[>>26]] [30] [CODE(DOMi)@en[[[EventSource]]]] の[[接続]]が開いている間に同オブジェクトが[[ごみ収集]]される場合、当該オブジェクトによって開かれた [[fetch]] は中断しなければ[['''なりません''']]。 [SRC[>>26]] ** 歴史 [REFS[ - [69] [CITE@en[Web Applications 1.0 r2870 Drop . Replace the API with an EventSource object. Rename onclosed to onclose on WebSocket. Remove the mostly worthless event definitions. I'll add more useful intro text to replace them later. Defined garbage collection specialness for WebSocket and EventSource.]] ([TIME[2009-02-26 17:33:00 +09:00]] 版) ]REFS] * Web Sockets [32] [CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]は[[イベント・ループ]]によって直近の[[タスク]]が実行開始された時点の [CODE(DOMa)@en[[[readyState]]]] について、 - [33] [CODE(DOMc)@en[[[CONNECTING]]]] であれば、 [CODE(DOMm)@en[[[open]]]], [CODE(DOMm)@en[[[message]]]], [CODE(DOMe)@en[[[error]]]], [CODE(DOMm)@en[[[close]]]] - [34] [CODE(DOMc)@en[[[OPEN]]]] であれば、 [CODE(DOMm)@en[[[message]]]], [CODE(DOMe)@en[[[error]]]], [CODE(DOMm)@en[[[close]]]] - [35] [CODE(DOMc)@en[[[CLOSING]]]] であれば、 [CODE(DOMe)@en[[[error]]]], [CODE(DOMm)@en[[[close]]]] のいずれかの[[事象聴取器]]が登録されている場合には、[[ごみ収集]]しては[['''なりません''']]。 [SRC[>>31]] [36] また、 [CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]は、 [[確立された接続]]があってネットワーク転送するデータがキューにある場合には[[ごみ収集]]しては[['''なりません''']]。 [SRC[>>31]] [37] [CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]が[[接続]]が開いたまま[[ごみ収集]]される場合は、 [RUBYB[[[閉じ]]][Close]]メッセージの[[状態符号]]なしで[RUBYB[[[WebSocket閉じハンドシェイクを開始]]]@en[start the WebSocket closing handshake]]しなければ[['''なりません''']]。 [SRC[>>31]] ** 歴史 [REFS[ - [70] [CITE@en[Web Applications 1.0 r2870 Drop . Replace the API with an EventSource object. Rename onclosed to onclose on WebSocket. Remove the mostly worthless event definitions. I'll add more useful intro text to replace them later. Defined garbage collection specialness for WebSocket and EventSource.]] ([TIME[2009-02-26 17:33:00 +09:00]] 版) ]REFS] * 歴史 [1] [[Web]] における[[ごみ収集]] ([[garbage collection]]) は [[HTML Living Standard]] ではじめて明示的に規定され、 その後他の仕様でも定義されるようになりました。 [REFS[ - [71] [CITE@en[Web Applications 1.0 r7573 Simplify the Blob release mechanism, at the cost of exposing the Web's innards a bit more to script authors.]] ( ([TIME[2012-12-08 07:57:00 +09:00]] 版)) - [72] [CITE@en[Navigation Timing]] ( ([TIME[2012-12-13 06:51:35 +09:00]] 版)) ]REFS] * メモ [73] [[オブジェクト]]が[[著者]]がアクセスできる範囲の外から[[参照]]されることもあり得ます。例えば [CODE(JS)@en[[[console.log]]]] [[メソッド]]に[[オブジェクト]]を渡すと、 その環境の[[デバッガー]]が当該オブジェクトへの参照を保持し、 [[スクリプト]]内から参照しなくなったとしても[[ごみ収集]]されずに残り続けることがあります。 [75] [CITE@en-US[Incremental GC in Firefox 16! | JavaScript]] ( ([TIME[2013-04-09 05:16:01 +09:00]] 版)) [76] [CITE@en[JavaScript:SpiderMonkey:GC Futures - MozillaWiki]] ( ([TIME[2013-04-02 17:53:25 +09:00]] 版)) [77] [CITE[substructural: Programming, Entrepreneurship, and the Markets: Garbage Collection in JavaScript, Part 1]] ( ([TIME[2013-03-20 10:45:20 +09:00]] 版)) [78] [CITE@en[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