理系学生日記

おまえはいつまで学生気分なのか

Java における class sun.reflect.GeneratedSerializationConstructorAccessor... または Java の Inflation について

まぁ GC の様子を見ていると、

Unloading class sun.reflect.GeneratedSerializationConstructorAccessor1
Unloading class sun.reflect.GeneratedSerializationConstructorAccessor2
Unloading class sun.reflect.GeneratedSerializationConstructorAccessor3

というようなログが大量に出てくるわけですが。
おいおいこんなクラス使ってねぇよと思ったので調べてみた。

結論

結論としては、これらのクラスはリフレクションを使う際に Java 側が動的に生成したクラス。それらが使用されなくなった(あるいはウィークリファレンスとしてしか残らなかった)ために GC で開放されたことを示してる。
末尾の番号が 3,000 とかいってるから一体全体どうしたものかと思っていたのだけれど、これは Inflation と呼ばれる Java の機能によって生成されたみたい。

Inflation とは

このあたりが流れとしては分かりやすいのだけれど。

昨今の Java で書くアプリケーションというのは、もはや Reflection を使っていないというのはほとんど無い。
フレームワーク使っているものについては基本的にフレームワーク内で Reflection 使いまくりだし、シリアライズ/デシリアライズの過程でも(ぼくの理解では)Reflection 使ってる。
で、この Reflection を使ってアクセサを用いるときの話なのだけれど、JVM がアクセスするための方法として

  • JNI
  • Bytecode

の 2 種類がある。
JNI の方は、使用するまで(Setup)の負荷はかからないけど、意外と遅い。これは Java → ネイティブコードへのコンテキストスイッチが生じるため。
一方で、ByteCode の方はコンパイル等が生じるから Setup の負荷はそれなりに大きいけれど、一度クラスとして作ってしまうと、それからのアクセスは早い。

このように、それぞれメリット/デメリットがあるわけで、じゃぁ JVM はどうやって処理しているかというと、あらかじめ閾値を設けておいて、特定クラスへのアクセスをするときにその閾値に達するまでは JNI を、そしてその閾値に達したあとは Bytecode を使うという動きをする。Bytecode アクセサに変わることを Inflation と呼ぶわけね。で、その Bytecode をつくるときに生成されるのが、上の sun.reflect.GeneratedSerializationConstructorAccessor(\d+) だったりするわけ。他のクラスもある。

Inflation のデメリット

Inflation、基本的に早いので良いんだけど、クラスを動的に生成しまくる。まぁほとんどの場合、問題にならないというか、ここをチューニングするんだったらもうちょっとチューニングすべきところがあるんじゃないですか?????という話ではある。ただ、あまりにクラス生成が激しすぎると、Permanent 領域を圧迫してきたりする。
こういうデメリットを調整するために、いちおう手は用意されていて、

-Dsun.reflect.inflationThreshold=N

なんていうコマンドライン引数を JVM に与えてあげれば、上で書いた「閾値」を調整できる。たしか 0 以下であれば Inflation は使用されない。つまり、ずっと JNI でアクセスするようになる。
ダマしっぽいのに

-Dsun.reflect.noInflation=true

なんてのもあるんだけど、この「noInflation=true」は Inflation は使用せず、ずっと ByteCode を使用するという挙動になる。ダマしっぽい。