◾️はじめに
https://dk521123.hatenablog.com/entry/2018/01/01/200400
でJavaのガベージ・コレクション(GC)について扱った。 基本的にGCは、時間が掛かるため、 Javaでは効率よくGCできるようにメモリ構造を工夫してある そこで今回は、Javaのメモリ構造について扱う
目次
【0】Javaのメモリ構造 【1】Heap Area 0)イメージ図 1)New領域 (Young領域) 2)Old領域 (Tenured領域) 【2】Native memory 1)補足:パーマネント領域 (非ヒープ領域) 【3】メモリのライフサイクル 0)おさらい:GC種類 1)メモリのライフサイクル解説 2)注意点
【0】Javaのメモリ構造
* Javaのメモリ構造(Java VM固有領域)は以下のように分かれている => Javaにおけるガベージ・コレクションのメカニズムを理解するには、 まずヒープ領域の構造を知っておく必要がある
| メモリ種類 | 解説 |
|---|---|
| ヒープ領域(Heap Area) | Javaプログラムのリソースを管理する領域 |
| Native memory | OSが管理する領域 |
【1】Heap Area
* JVM(Java Virtual Machine、Java VM)により管理される
0)イメージ図
# ヒープ領域は大きく分けて以下の2つの領域で構成 +--------------------+---------+ | New領域 | Old領域 | |[ Eden ][ S0 ][ S1 ]|[Tenured]| +--------------------+---------+
各領域に関して
* ざっくり言うと、以下のような感じ
| 領域名 | 説明 | メモ |
|---|---|---|
| Eden領域 | 短命なオブジェクトのための領域 | |
| Survivor領域 | 中期間生きているオブジェクトのための領域 | cf. Survivor(サバイバー)= 生き残り |
| Tenured領域 | 長期間生きているオブジェクトのための領域 | cf. Tenured(テニュアード)= 終身雇用が保証されている |
1)New領域 (Young領域)
New領域 (Young領域) の構成
| 名称 | 説明 | メモ |
|---|---|---|
| Eden領域 | 最初に割り当てられるメモリ領域 | |
| Survivor 0 領域(S0。From)/Survivor 1 領域(S1。To) | マイナーGC後に解放されない、かつOldにも行かないデータのためのメモリ領域 | 便宜的にS0,S1と置いているだけとか |
2)Old領域 (Tenured領域)
* New領域を対象としたマイナーGC後に生き残ったオブジェクトが配置される * 配置されたオブジェクトは、ランタイム中に何度も利用される 寿命の長いオブジェクトとして判断されたもの cf. Tenured(テニュアード)= 終身雇用が保証されている
【2】Native memory
* OSが管理する領域
| 領域名 | 説明 | メモ |
|---|---|---|
| Metaspace領域 | Javaのクラス、メソッドなど永続的に参照される静的オブジェクトを管理する領域 | メモリの許す限り消費してしまう |
| Cヒープ領域 | JavaVM自身のリソースを管理する領域 | |
| スレッドスタック領域 | スレッド毎に保持するスタック領域 |
1)補足:パーマネント領域 (非ヒープ領域)
* Java8からは外された => 確かに「java -XX:PermSize=128m -XX:MaxPermSize=128m」 ってコマンドを実行しようとすると「support was removed in Java8.0」 って言われる... * 以下のケースで、 Permanentサイズを大きく設定する必要がある [1] ライブラリやフレームワークを利用する場合 [2] JSP(Java Server Pages)を大量に使用する場合 など
Permanent領域が不足すると...
* FullGCが行われ、必要に応じてクラスの読み直しが発生 * メモリが枯渇するとOutOfMemoryが発生
【3】メモリのライフサイクル
0)おさらい:GC種類
| GC種類 | 説明 | メモ |
|---|---|---|
| マイナーGC / Scavenge GC / Young GC | New領域のみのガベージ・コレクション | cf. Scavenge (スカベンジ) = ゴミ拾い |
| メジャーGC / Full GC | NEWとOLD両方の領域を対象としたガベージ・コレクション |
* 詳細は以下の関連記事を参照のこと
Java 〜 ガベージコレクション 〜
https://dk521123.hatenablog.com/entry/2018/01/01/200400
1)メモリのライフサイクル解説
| # | 契機 | イベント |
|---|---|---|
| 1 | オブジェクト新規作成 | Eden領域への移動 |
| 2 | Eden領域不足 | Scavenge GC の実行。使用されているオブジェクトはSurvivor領域への移動 |
| 3 | Scavenge GC を一定数繰り返した後 | Old領域へ移動 |
| 4 | Old領域不足 | Full GC の実行 |
[1] オブジェクト新規作成
* オブジェクトが新たに作成されると そのオブジェクトは、まず Eden(E)領域に配置される
[2] Eden領域不足
* Scavenge GC が実行され、使用されていないオブジェクトは、破棄される => Eden領域をクリアし、再び新しいオブジェクトを受け入れるようになる * 現在も使用されていると判断されたオブジェクトは、 Survivor1(From)領域 または Survivor1(To)領域へ移動し、 メモリー上に残されるオブジェクトの候補となる => インスタンスが不要になるまでの間、 From領域とTo領域の間でインスタンスの移動を繰り返す
[3] Scavenge GC を一定数繰り返した後
* Scavenge GC を一定回数(MaxTenuringThresholdの値でデフォルトは32) 繰り返した後で、Java VMによって使用頻度が高いと判断されたオブジェクトは、 破棄されずにOld領域へ移動する => 「長時間使っているインスタンス」=「今後も破棄される確率が少ないインスタンス」 としてOld領域に置いとく。 => 短時間で不要なインスタンスを頻繁に行い、ガベージ・コレクションを効率よく行う。
[4] Old領域不足
* Old領域が不足すると、Full GCが行う => Old領域に退避されていた不要なオブジェクトの破棄を最優先する
2)注意点
[1] Scavenge GC に関して
* Scavenge GCは、頻ぱんに起こるのが普通だが、 少なからずパフォーマンスに影響するので、 あまりにも多く発生する場合は、New領域を増やすことも検討する必要がある
[2] Full GC に関して
* Full GCが実行されると、ランタイムがいったん停止("Stop the world")
* Full GCが頻発すると、パフォーマンスが悪くなるため
可能な限りFull GCが発生しないよう、Javaランタイムのヒープ・サイズを調整
参考文献
http://thinkit.co.jp/story/2011/03/25/2054/page/0/2
http://www.itmedia.co.jp/enterprise/articles/0905/27/news002_2.html
http://gihyo.jp/dev/serial/01/jvm-arc/0007
http://blog.pepese.com/entry/20120508/1336467306
http://chonaso.hatenablog.com/entry/20150917/1442442000
関連記事
Java 〜 ガベージコレクション 〜
https://dk521123.hatenablog.com/entry/2018/01/01/200400
JVMのメモリ情報を出力させる
https://dk521123.hatenablog.com/entry/2014/04/08/211143
Javaにおけるオブジェクトの使用メモリサイズの測る
【フリーツール】【Java】メモリ監視ツール ~Eclipse Memory Analyzer~
Instruction の getObjectSize で、オブジェクトの使用メモリサイズを測る