世代別GC

先日ちょいと書いたJAVAWORLDのお話の続き。本当に読みたかったのはこの世代別GCについて。以下やはり殴り書きメモ。

Q.オブジェクトは使い回したほうがGCの対象にならないので速いってホント?
A.ウソ(一概にそうとは言えない)

  • 世代別コピー式GC(っちゅうのがある):ヒープ領域(オブジェクトの管理メモリ)が新世代と旧世代に分かれている。

〜新世代領域〜
生存期間の短いオブジェクトを管理。さらにそれぞれエデンとサバイバと呼ばれる領域に分かれる。

 エデン:新しいオブジェクトはまずここに割り当てられる。エデン領域がいっぱいになるとスカベンジGCが実行される。
 サバイバ:さらに2つの領域に分かれる(便宜上S0,S1と呼ぶことにする)。


〜旧世代領域〜
生存期間の長いオブジェクトを管理。スカベンジGC回数のしきい値を超えたオブジェクトが移される(殿堂入り)する


☆スカベンジGC
・初回スカベンジGCではエデン領域に存在するオブジェクトにおいて、まだ参照が生きているもの(ライブオブジェクト)をS0に移し、それ以外は削除→つまりエデンは空になる
・再びエデン領域がいっぱいになると2回目のスカベンジGCが実行。今度はエデン領域のライブオブジェクトと、S0のライブオブジェクトがS1に移され、それ以外は削除される。
・以降、S0とS1の間で「ライブオブジェクトを移動&死んでるヤツ削除」を繰り返す。
・スカベンジGCが繰り返される中でしきい値(MaxTenuringThreshould)を超えたものは殿堂入り(Sun用語)の対象となり、旧世代領域に移される。
☆フルGC
・旧世代領域がいっぱいになった場合にフルGCが行われる。フルGCは対象となるオブジェクトの範囲が広いのと、オブジェクトの配置を整理(デフラグのイメージ?)しながらやることから時間がかかる。


以上言葉だけだとイメージしづらいけど自分で図を用意するのはめんどい、というわけで図を探した。あれこれあったけれども、次のHPの資料(pdf)の29ページ辺りからが一番まともっぽい。


HP-UX JAVA パフォーマンスチューニング


んで、
これらのことからオブジェクトの使いまわしの話に関しての記事での論旨は、フルGCをやらせちゃうと時間がかかるんだから、オブジェクトの使いまわしはGCによるパフォーマンス低下の対策としては無意味ですよ、と締めくくっている。
これについて、会社のかなりできる方にお尋ねしたところ、たしかにスカベンジGCが時間にして「コンマ何秒」レベルであるのに対してフルGCは「数秒以上」と、アプリが固まったことをユーザが体感してしまうレベル(もっとヤバイときもある)。だからこのお話は正しいとのこと。しっかしじゃあフルGC減らしゃーいいのかってーとこれが(状況によるけど)そうそう減らせるもんでもなく、現実的にはGC周りのチューニングはかなり難しい話であるとのこと。うむむ。

さらにJAVAWORLDに書いてあることだけじゃあれこれ説明が足りなくて、次辺りを参照するとさらにいろいろわかる(実は上の部分にも少し加えてある)。


@IT 事例に学ぶWebシステム開発のワンポイント(6)-GCをチューニングしよう-


この記事では、やはりフルGCはイクナイ!!ということで具体的な回避策にも触れていたり(但しこちらはスカベンジGCについてかなりあっさり)。注目すべきは「Tenure=32を死守するために」という章のちょい前辺りからの話。なんかしきい値が変わってしまうような記述があるのだけれど、なんでそういうことになるのか謎。実装ですか、そうですか。おそらくこの辺りが先に述べた「GCチューニングは難しい」という話に繋がるのかなあ、という雰囲気だけ感じた。あとパーマネント領域なんてのも出てくるけどまだ調べてない。
本当はスカベンジGCについてどうにも辻褄が合わないというか上手く説明できない事例を思いついてしまったのだけど、たぶんオレの理解が足りないせいっぽいのでもう少し調べる&考える必要あり。とりあえずなんでもオブジェクト使いまわせば良いってもんじゃないことは間違いない模様。シングルトンシングルトン言うな、と。
んで今書いててすげー気になりだしたのは「フルGCの対象となる奴らはみな強者ばかりのはず。そうなると旧世代領域いっぱいだけどもみんな参照健在な場合、どうすんのよ?」みたいな。


そんなわけで追記:コメントにあるとおり「Out Of Memory」でFA。