jsのクロージャのキャプチャ範囲
発端
Claude Code (JSで書かれたCLIツール)のメモリ使用量を削減した話
XユーザーのJarred Sumnerさん: 「Here's a 1 GB memory reduction for very long Claude Code sessions Before: () => controller.abort() Fix: controller.abort.bind(controller)`」 / X
Xユーザーのmattnさん: 「あー、アロー関数は周辺をキャプチャしてしまうから GC されないって事か。なるほど。」 / X
code:js
//before
const f = () => controller.abort();
//after
const f = controller.abort.bind(controller);
一見同じに見えるが、後者がcontrollerだけをキャプチャするのに対し、前者はcontroller以外の変数(今回の例だとthis, init, optionsなど)もまとめてキャプチャしてしまうらしい
Xユーザーのzakkiさん: 「@kazuho JavaScriptでの実装が大変&仕様上許されてるっぽく https://t.co/pqt9BysQkU」 / X
XユーザーのRob Palmerさん: 「All browser engines implement GC imprecisely. The JS spec permits this. In a scope with 2 inner functions (A & B) that become long-lived closures where: A captures C B captures D Both closures extend the lifetime of the *union* of the scope's captured bindings (C & D)」 / X
XユーザーのKazuho Okuさん: 「@k_matsuzaki なるほどです。JSで同じことやるなら、変数を、共通のクロージャで利用される組に分けて、それぞれ別のGCオブジェクトにするみたいになると思いますが、最悪計算量が指数オーダーな気がして、外部の信頼できないコードを迅速に実行開始しなければならないというJSの要件を満たすのが難しそうです」 / X
Xユーザーのmod_poppoさん: 「これJavaScript処理系の実装が最適化不足かECMAScriptの言語仕様に欠陥があるように思えるんだけど、詳しい人の解説が欲しい」 / X
XユーザーのFUJI Goroさん: 「実装のバグにみえますけどね。処理系によっても挙動が違うんじゃないだろうか。 @__sosukesuzuki どうなんですかこれ。」 / X
Xユーザーのsosukeさん: 「@__gfx__ 自分では判断がつきませんでしたが、JSCの現在のGCを設計した方はバグだと考えているようです https://t.co/leqLcxCEMM」 / X
XユーザーのFilip Jerzy Pizłoさん: 「@jarredsumner Seems like a JSC bug Shouldn’t capture this if the function doesn’t use it Your workaround is dang tho」 / X
XユーザーのFilip Jerzy Pizłoさん: 「@jarredsumner Yeah. I *think* that’s just JSC’s closure conversion being pessimistic. It’s a super cool bug. In the FP world they call this bug “not being safe for space”. While working on JSC I was always hoping I’d find a real world example of not being safe for space, but never did.」 / X
XユーザーのMiura Hidekiさん: 「意外とクロージャで一部の変数をキャプチャするのめんどくさいよねと言う話をGrokとしてみた https://t.co/hI1KuFWmWH」 / X
まとめ
1. 変数を一個一個キャプチャすると、正確だけど遅い
2. 変数をまとめてキャプチャすると速いが、でかいオブジェクトが混ざっていたときにメモリの使いすぎになる
というトレードオフがある。