Expand description
参照カウントでは回収できない循環ゴミの回収(試行削除 / trial deletion)。
【2026-10-19】JS の値は Rc<RefCell<…>> の参照カウントで管理しており、
「関数呼び出しのスコープ → 中で作った関数 → そのスコープ」のような循環は
ページを閉じるまで解放されなかった。YouTube の視聴ページ(kevlar_base 10.8MB)の
実行中に 512MB のヒープを使い切っていた。
§方式
生きている Obj と Scope をすべて台帳(Registry)に登録しておき、回収時に
- 台帳の全節点について、節点どうしの参照(辺)の本数を数える(内部参照数)。
- 強参照数 − 内部参照数 > 0 の節点は、台帳の外(Rust のスタック・大域・DOM 側の リスナ等)から参照されている「根」。
- 根から辺をたどって届く節点に印を付ける。印の無い節点は外から届かない循環ゴミ。
- ゴミの中身(プロパティ・変数・原型など)を空にして循環を断ち、参照カウントで 解放させる。
§安全性
- 数える辺は「実在する強参照」だけ(数え漏れは安全側: その先の節点が根に見える)。
共有され得る中間の
Rc(Promise の状態など)の先は数えない。 - 借用中で中身を読めない節点は根として扱い、その先も数えない(同じく安全側)。
- Rust のスタックが保持している値は強参照数に表れるので、実行の途中でも回収できる。
- JS は 1 つのスレッド(ブラウザのアプリのプロセス)でしか動かない前提。台帳自体は spin ロックで守るが、回収は台帳の全節点を触るので、別スレッドで同時に JS が動く 構成になったら台帳をスレッドごとに分ける必要がある。
Obj・ScopeをRcから取り出したり、中身ごと入れ替えたりしてはならない (台帳の番地と中身の番号がずれる)。
Structs§
Enums§
- Node 🔒
- 台帳の節点(一時的に強参照を 1 本持つ)。
Constants§
- MIN_
INTERVAL 🔒 - 回収の最小間隔(登録数)。
- NO_SLOT
- 未登録を表す台帳の番号。
Statics§
Functions§
- children 🔒
- 節点が直接持つ強参照の先(節点番号)を
outに積む。読めなければ false。 節点番号はObjが台帳の番号、Scopeがn_objs + 台帳の番号。 番号を読めない(借用中・未登録の)先は積まない(安全側)。 - collect
- 循環ゴミを探す。
freeが真なら回収もする(偽なら数えるだけ)。 - maybe_
collect - 頃合いなら回収する。
- put 🔒
- should_
collect - 回収する頃合いか(呼び出し経路から頻繁に呼ぶので原子変数だけ見る)。
- track_
obj - 作った
Objを台帳に載せる(value.rsの構築関数が呼ぶ)。 - track_
scope - 作った
Scopeを台帳に載せる。 - untrack_
obj Objの解放時に台帳から外す。- untrack_
scope Scopeの解放時に台帳から外す。