関連する質問: Java インナークラスと比較した Scala クロージャー -> final VS var
Scala がクロージャーに取り込まれた変数を、スタックではなくヒープ上でライブにするのはいつなのだろうか。Martin Odersky の Scala の本を読んでいますが、今のところこの情報は見つかりませんでした。誰かがボンネットの後ろにあるものを説明できますか?
関連する質問: Java インナークラスと比較した Scala クロージャー -> final VS var
Scala がクロージャーに取り込まれた変数を、スタックではなくヒープ上でライブにするのはいつなのだろうか。Martin Odersky の Scala の本を読んでいますが、今のところこの情報は見つかりませんでした。誰かがボンネットの後ろにあるものを説明できますか?
scala の無名関数 (および実際には任意の関数) は、実際にはオブジェクト (のインスタンスFunction*
) です。インスタンス化されると、関数オブジェクトの内部フィールドに値をコピーすることによって、値の取得が行われます。関数本体 (つまり、関数オブジェクトのapply
メソッド) では、キャプチャされた val へのアクセスは、これらのフィールドにアクセスすることによって行われます。
変数のキャプチャは似ていますが、コンパイラが間接的なレベルを追加する必要がある点が異なります。var 値は、隠し可変ホルダー (単純に、var の現在の値を指す可変フィールドを持つオブジェクト) を介してアクセスされ、これがこれです。関数オブジェクトにコピーされるホルダー。(ローカル コードまたは関数オブジェクトによって) var に書き込む場合、書き込まれるのは所有者のフィールドです。このメカニズムにより、ローカル コードと関数のコードが同じデータを操作し、両方が互いの変更を認識できるようになります。
したがって、答えは、キャプチャされた val とキャプチャされた var の両方が常にヒープ上にあるということです (直接関数オブジェクトのフィールドとして、またはいくつかのラッパー オブジェクトのフィールドとして)。
コンパイラの内部はわかりませんが、これを行う方法は次のとおりです。ローカル変数ごとに、コンパイラは false に初期化されたフラグを維持します。変数が使用されるたびに、コンパイラは、変数の宣言を含まないクラスまたはクロージャー内で使用されているかどうかを確認します。その場合、フラグは true に設定されます。変数のスコープの最後で、フラグがまだ false の場合、変数はスタック上に存在できます。それ以外の場合は、ヒープ上に存在する必要があります。