JavaやScalaのクロージャと無名クラスとメモリリークとSIP-21のSpores

"Scala Improvement Process" という新機能などの提案のための機構があって、最近SIP-21「Spores」というものが提案されました。

http://docs.scala-lang.org/sips/pending/spores.html

最終的にそれの話をするのですが、その前に前提としてJavaScalaクロージャや無名クラスの話をするのと、それらについて調べたら自分も知らなかった事実が発覚したのでそれについても書きます。
まず、Javaの話。ちなみに環境は1.7.0_21です。
以下のようなclassを用意して、ScalaのREPLでFの無名クラスについて調べてみます。

以上から、

  • Fの無名クラスは、コンストラクターで外部のクラスのインスタンスを受け取る
  • かつ、それをfieldに保持している
  • Fの無名クラスは「IntegerからStringの変換」という処理のみを行なっていて、外部のAクラスのインスタンスの環境にアクセスしないのにも関わらず、上記2点のような無駄な処理を行う

ということがわかります。また

  • この「外部のクラスのインスタンスを保持する」という動作は、Javaの言語仕様的に定められていたはず*1で、普通は変更できないはず
  • Java8のラムダの場合にどうなるのかは知らないので、その辺りの仕様知ってたら誰か教えてください


さて、(今まで自分も知らなかったのですが)同じことをScalaでやると、なんとScalaコンパイラのほうが頭がいいみたいです。

以上のようにScalaでは、Functionオブジェクトの場合も無名クラスの場合も
「外部の環境にアクセスする場合のみ、外部のクラスのインスタンスを内部のprivateなfieldに保持」
するようです。


さて、(Java8の話は抜きにして)ScalaでもJavaでも、内部実装的には所詮単なる無名クラスでクロージャ相当のことをしてます。
当たり前ですが、Scalaでも、「外部の環境にアクセスする場合」つまり「外部のclassのインスタンスにアクセス」できないといけないので、その場合外部のクラスのインスタンスをフィールドに保持します。

しかし、これはJavaでもScalaでもメモリリークにつながります。外部のクラスのインスタンスの一部にアクセスしたいだけだとしても、外部のインスタンスを丸ごと保持してしまうので、内部のクラスのインスタンスGCされるまで、外部のクラスのインスタンスGCされません。

アンドロイドのプログラミングでは、(VMがあまり賢くないことも影響して)そういうパターンでメモリリークするのは結構有名ですよね?




さて、どうもそれらの
メモリリークなどの問題*2を解決するための仕組みを入れよう!」
というのが、今提案されている、SIP-21のSporesのようです。

はじめに断っておくと、
まだ提案段階で、実装も全く始まっていないし、そもそも実装すること自体も決定していないはずです。始まったようです。また、自分の解釈が間違ってたら、ぜひツッコミをお願いします。

で、Sporesとはなにか?を簡単に言ってみると

  • マクロで実装する予定?
  • 最終的にやりたいことは、単にクロージャである
  • 今まで説明したメモリリークの問題を避けるため、ある程度の制限がある
  • 外部のアクセスしたい環境(変数)を、あらかじめvalで明示的に宣言しなくてはならない*3
  • そうすると、外部のクラスのインスタンスは保持せずに、明示的に宣言したもののみを保持する

ということだと思います。

提案されたばかりで今後色々変わるかもしれないし、詳しいこと書こうにもSIPの英語を正しく読み取る自信がないので、これくらいにしておきますが、果たして入るんでしょうか・・・。入るとしても、具体的な実装や、入る時期(version2.11 か?)はどうなるんでしょうか。

*1:間違ってたらツッコミください

*2:リアライゼーションや、mutableな変数を保持することによって発生するRace conditions、なども問題点としてSIPのページに書いてあります

*3:つまりvarは不可能で、valのみなので、多分正確にはクロージャというと語弊がある。SIPのページにも「which enables safer use of closures in concurrent and distributed environments. This is achieved by controlling the environment which a spore can capture. Using an assignment-on-capture semantics, certain concurrency bugs due to capturing mutable references can be avoided.」とある