Java/GC

Last-modified: 2013-02-03 (日) 23:38:48

仕組み

メモリ領域

次のページが明快で詳しい

http://d.hatena.ne.jp/tanakakns/20120508/1336467306

jconsoleでの表示名称では

ヒープ
  Eden           ┐
  Survivor       ┘2つあわせてNew領域
  Tenured        ─ Old領域
非ヒープ
  Code Cache
  Perm Gen [shared-rw]
  Perm Gen [shared-ro]
  Perm Gen
  • SurvivorはFrom/Toの二つの同サイズのメモリ領域からなるが、jconsoleで表示しているサイズはそのうち1個分のようだ。
  • 表示名称はjconsoleが決めているというよりは、監視対象のJVM内のMBeanが提供する名前をそのまま表示しているのかも。
  • 3つに分かれているPerm Genがそれぞれ何なのかが不明。

New世代GC(ScavengeGC、マイナーGCともいう)

http://www.atmarkit.co.jp/fjava/rensai3/javavm02/javavm02_1.html

新規オブジェクトは、Edenからメモリを割り当て。
やがてEdenがいっぱいになり、いっぱいになるとNew世代GCが起動。

New世代GCはCopying方式

  • Edenから使用中のオブジェクトのみ選んでSurvivorのToにコピー。
  • Survivor内でも、Fromから使用中のオブジェクトをToにコピー。
  • EdenとFromはクリア。
  • ToとFromの役割(名前)を入れ替え。

以下の場合にSurvivorのToからTenured(Old領域)に移動する。

  • コピーの回数が一定回数を超えたオブジェクト
  • Toがあふれた場合?

ここでの「一定回数」は、1から-XX:MaxTenuringThreshold指定値の範囲で動的に決定される。

実行中、アプリの動作は停止。

jconsoleで観測すると、

  • Edenは、0付近から100%付近までだんだん使用率があがり、0付近に戻る、というのを繰り返す。
  • Survivorは、New世代GCが実行されたタイミングで変化。Edenから移ってくれば増えるし、Tenured(Old領域)に移動すれば減る。

Survivorとして表示されるサイズ(使用済み、確定、最大)は、From/Toに分割されたバッファ1つ分のサイズの模様。

Old世代GC(Full GCとも)

http://www.atmarkit.co.jp/fjava/rensai3/javavm02/javavm02_1.html
mark-sweep-compact方式

Old領域の中で、使用中のオブジェクトにマークを付け、未使用のものを削除し、空いた隙間を詰める。
実行中、アプリの動作は停止。

 

なお、コンカレントGCを使用した場合は挙動が異なり、停止時間が短くなる。
http://www.atmarkit.co.jp/fjava/rensai4/troublehacks02/troublehacks02_1.html

設定

サイズの指定

  • -XX:NewSize~, -XX:MaxNewSize
    初期,最大New領域の初期サイズ
  • -XX:SurvivorRatio
    EdenのサイズをSurvivor(To1個分)で割った値。なお、ToとFromは同じサイズが割り当てられる。
    例えば、NewSizeが10mで、SurvivorRatioが8の場合、Eden:To:From=8:1:1 で分割されるので、Edenが8メガバイト、ToとFromがそれぞれ1メガバイトとなる。省略時は、試したVMでは8であった。OSやヒープ総量によって変わるのかも。
  • -XX:PermSize, -XX:MaxPermSize
    初期,最大Permanent領域サイズ
  • -Xss
    スレッドスタックサイズ

その他の設定

  • -XX:TargetSurvivorRatio
    Survivor領域がいっぱいだと判断される使用率
  • XX:MaxTenuringThreshold
    New領域内でコピーされる回数の閾値の最大値。上述のNew世代GCの説明を参照。
  • XX:+DisableExplicitGC
    System.gc()を無効化

System.gc()

  • jconsoleで観察しているところでは、Survivorの内容が Tenured Genに移動し、Survivorが空になるようだ。Edenの分もTenured Genに移動しているかもしれない。そうだとすると、ある程度の時間を経れば未使用となるオブジェクトもTenuredに移動してしまうことになり、その点では、不利になる。
  • Old世代GCも走る。

finalizer

finalizerを持つオブジェクトは1回目のGCでfinalizer実行対象だとマークされ解放はされず、finalize完了後のGCで解放されるとのこと。したがって、GCの頻度が低いと、不要なのに解放されずにたまる状況が発生するらしい。

http://www.atmarkit.co.jp/fjava/rensai4/troublehacks09/troublehacks09_3.html

メモ

  • GCログを取得するオプション
    -Xloggc:/path/to/file.log -XX:+PrintGCDetails
  • リフレクションで自動生成されるクラスによりPerm領域を圧迫する事例
    「強制FullGCを実行することでアンロードすることは可能」
    http://d.hatena.ne.jp/Kazzz/20080208/p1