Java仮想マシン(JVM)を読解しながら理解する #06
こちらの記事の続きです
よんでいる公式ドキュメント
今週末は今更ながらにClaude Codeをプロプランに切り替えたのですが、
想定より楽しくなってしまいずっと触っていたらあっという間に深夜になってしまいました・・・😇
GitHub Pagesにも公開しているのでよかったら観てみてください!!
もう少し手を加えたい点はいくつかあるのですが、頭の中にあったことはだいたい表現できてきています!!(最初の読み込みに時間がかかる問題がありますが。。。)
余談はさておき本題にはいります・・・・!!
2.5.3. Heap(ヒープ)
Javaを学習したらどのテキストにも必ず登場する単語なので恐らく初見の人はすくないはず・・・
けど、データ領域である 以外の説明ができない人も多いはず・・・
ヒープとは??
•ヒープ(Heap) は、JVM が用意する実行時のデータ領域のひとつ
•すべての オブジェクトインスタンスや配列 が、このヒープ領域からメモリを確保されます
•全スレッドで共有される領域
一応データ領域について整理しておきます!!
領域 | 共有/スレッド単位 | 主な役割 | 例外 |
---|---|---|---|
Heap(ヒープ) | 共有 | オブジェクト・配列の格納。GCによる自動管理対象 | OutOfMemoryError: Java heap space |
Method Area(メソッド領域) | 共有 | クラス情報、メソッド、フィールド、定数プールなどを格納。Java 8以降はMetaspace | OutOfMemoryError: Metaspace |
PC Register(PCレジスタ) | スレッドごと | 現在実行中の命令位置を保持。Javaメソッド実行中はバイトコードのオフセットを記録、native 実行中は未定義 |
なし |
JVM Stack(Java仮想マシンスタック) | スレッドごと | メソッド呼び出しごとにフレームを積む。フレームにはローカル変数、計算途中の値、戻り先などが含まれる |
StackOverflowError , OutOfMemoryError
|
Native Method Stack(ネイティブメソッドスタック) | スレッドごと |
native メソッドの実行に利用。C/C++などJVM外の処理を扱う |
OutOfMemoryError |
ヒープのライフサイクル
•JVM起動時(サーバーがプロセスとして立ち上がる時)に作成される
•JVM終了時(サーバープロセスを停止した時)に破棄される
•開発者が明示的に解放することはできない
GC(ガーベジコレクション)
不要になったオブジェクトを自動で解放し、メモリを再利用可能にすること
「不要」とはどう判定されるのか?
JVMでは 「参照が到達可能かどうか」 で判定
状態 | 説明 | 例 | GC対象 |
---|---|---|---|
到達可能 (reachable) | ルート(root)から参照されているオブジェクト | - スタック上の変数 - クラスの静的フィールド - ネイティブコードの参照 |
❌ GC対象外(まだ利用中) |
到達不能 (unreachable) | どの参照からもたどれないオブジェクト | - ローカル変数のスコープを抜けた後のオブジェクト - 参照が null に置き換えられたオブジェクト |
✅ GC対象(不要とみなされる) |
なるほど・・・🤔
メモリを使いきってしまった場合はどうなるのだろうか??
達成可能な状態だどメモリが解放されないとなると
メモリを使い切ってしまった場合は・・・??
※後述の例外を参照
ヒープサイズ
ヒープサイズの初期値/最大値は「実行環境(物理メモリ量やOS)」によって変わるため、
デフォルトサイズや拡張方法は実装依存になる
実装(HotSpot, OpenJ9, GraalVMなど)が、
実行環境に応じて「初期ヒープサイズ」「最大ヒープサイズ」を計算をする
以下のコマンドを実行すれば自分の環境でJVMが使用しようとしているヒープサイズが確認できます!!
java -XX:+PrintFlagsFinal -version | grep HeapSize
私の環境で実行すると以下のような出力がありました。
size_t ErgoHeapSizeLimit = 0 {product} {default} // ヒープ上限の「エルゴノミクス設定」。0は「制限なし」を意味する
size_t HeapSizePerGCThread = 43620760 {product} {default} // GCスレッド1本あたりの目安サイズ(約41.6MB)
size_t InitialHeapSize = 134217728 {product} {ergonomic} // 初期ヒープサイズ(Xms)。128MBで起動
size_t LargePageHeapSizeThreshold = 134217728 {product} {default} // ラージページを使うかどうかの閾値(128MB)
size_t MaxHeapSize = 2147483648 {product} {ergonomic} // 最大ヒープサイズ(Xmx)。2GBが上限
size_t MinHeapSize = 8388608 {product} {ergonomic} // 最小ヒープサイズ。8MB未満には縮小しない
uintx NonNMethodCodeHeapSize = 5839372 {pd product} {ergonomic} // コードキャッシュ領域(非メソッドコード用 約5.6MB)
uintx NonProfiledCodeHeapSize = 122909434 {pd product} {ergonomic} // コードキャッシュ領域(プロファイルなしメソッド用 約117MB)
uintx ProfiledCodeHeapSize = 122909434 {pd product} {ergonomic} // コードキャッシュ領域(プロファイルありメソッド用 約117MB)
size_t SoftMaxHeapSize = 2147483648 {manageable} {ergonomic} // ソフト制限としてのヒープ上限。MaxHeapSizeと同じ2GB
例外
計算に必要なヒープ領域が確保できない場合、OutOfMemoryError がスローされる
OutOfMemoryError
例: OutOfMemoryError: Java heap space
まとめ
•ヒープは オブジェクトと配列を置く「共有の倉庫」
•JVM起動時に作成され、終了時に破棄される
•メモリ解放は GCが自動で管理 開発者は明示的に解放できない
•サイズは固定/動的いずれも可能で、JVM実装者が指定できる
•メモリ不足になると OutOfMemoryError が発生する
Discussion