JavaFX のローカル変数の面白げな側面(2)バインド ("bind") とトリガー ("on replace")

JavaFX のローカル変数の面白げな側面てことで、クロージャ (closure) に続いて、バインド ("bind") とトリガー ("on replace") の話が書かれてる。
Binds and Triggers on Local Variables | Synchronous Messages


因みに JavaFX のバインド ("bind") の基礎については以下参照
第1回 JavaFXの概要と基本:ついにベールを脱いだJavaFX|gihyo.jp … 技術評論社
トリガー ("on replace")の基礎については以下参照
第1回 JavaFXの概要と基本:ついにベールを脱いだJavaFX|gihyo.jp … 技術評論社


ソース

var v = 0;
function f(p: Integer):Void {
    var localvar = bind p + v on replace old {
        println("localvar: {old} => {localvar}");
    }
}
f(17);
f(32);
println(">>> increment v");
v++;
println(">>> increment v again");
v++;
println(">>> done!");


実行結果

localvar: 0 => 17
localvar: 0 => 32
>>> increment v
localvar: 17 => 18
localvar: 32 => 33
>>> increment v again
localvar: 18 => 19
localvar: 33 => 34
>>> done!


今回は内部関数ではなくて、関数 f の中でローカル変数 localvar にバインドとトリガーを設定している。
関数を呼び出すと、仮引数 p と スクリプト変数 v の加算結果がローカル変数 localvar に代入されてトリガーが起動されて、localvar の古い値と新しい値が出力される。
ここまでは、コードを見て何となく理解できるところ。
摩訶不思議に感じるのは、v++ が実行されると、変数 v の値が変わるので、バインドが働いて p + v の結果が localvar に反映される。それでトリガーが起動されると出力される値は、なんともまあ、前回呼び出したときに渡した値と、それに 1 を加算した値。(17 => 18 / 32 => 33)

でもこれって関数が呼ばれるたびに、ローカル変数 localvar がどこか知らないところに保存され続けるの?際限なく?という疑問が当然沸いてくる。

Well, maybe. The problem is that although we can observe localvar by placing a trigger on it, there is nothing external that references it. This seems pretty fragile, since things that don’t have references to them are subject to being garbage collected. Let’s test this by allocating a bunch of memory to force GC. Just before “increment v again” insert the following code:

Binds and Triggers on Local Variables | Synchronous Messages


というわけで、println(">>> increment v again"); の前に下のコードを追加して、GC を起こす実験を試みている。

var seq: String[];
for (i in [1..50000]) {
    insert "{i}" into seq;
}

環境によってループ回数は変わるかもしれないけど、50000回で毎回 GC が起こったとのこと。僕の環境でも50000回で起こった。
すると結果はこうなる。


実行結果

localvar: 0 => 17
localvar: 0 => 32
>>> increment v
localvar: 17 => 18
localvar: 32 => 33
>>> increment v again
>>> done!


あらまあ!さっきは出力された

localvar: 18 => 19
localvar: 33 => 34

が、GC を起こしたら出なくなった!


ということは、自分の管理下の変数に保持した参照ではない、どこか知らないところに保存されてた localvar は、GC に収集されてしまったわけか。
一見 Java の WeekReference/WeakHashMap を思い起こさせるけど、JavaFX のこれ使える?
間違って使ってしまうと、かなりデバッグ困難な状況になりそう。

This is admittedly a pretty obscure corner of the language. Why would anybody want to put a bind and a trigger on a local variable? In my next blog post, I’ll explain why this construct has come up repeatedly in real programs, how the GC issue has caused problems, and what to do about it.

Binds and Triggers on Local Variables | Synchronous Messages


これは JavaFX Script 言語のかなり不明瞭なコーナーケースであると認めざるを得ない、と。
また、続きがあるみたいだから、次も check it out とく。


ちなみに、前回のエントリ のコードに、GC を起こさせるループ(5万回と10万回)のコードを書いて実行してみたけど、今回起こったような現象にはならなかった。つまり、ローカル変数 localvar は GC の対象にはならなかった。
これらの現象は、意図されたものなのかな。それとも思わぬ副作用だったり?