SpringBoot(Java)のヒープメモリとスレッドスタック
調査した件の書き溜め。
SpringBoot(Java)のアプリケーションにおいて、ヒープメモリ以外でメモリを消費している要因としていくつか考えらるが、特にスレッドスタックが大きな要因である可能性が高い。
各スレッドは、スレッドスタックという独自のメモリ領域を持ち、このスタック領域はスレッドの実行コンテキストに関する情報、例えばメソッド呼び出しの履歴やローカル変数などを保持している。
もしアプリケーションが多数のスレッドを使用している場合、それぞれのスレッドが持つスタックのメモリ使用量が累計されることで、合計のメモリ使用量が大きくなる可能性がある。
特にThreadPoolTaskExecutorのようなスレッドプールを利用して非同期に多くのタスクを実行している場合、多くのスレッドが同時に動作しているとスレッドスタックの消費メモリは無視できない量になることが考えられる。
また、Javaプロセスのメモリ消費には、ヒープやスレッドスタックの他にもメタスペース、JITコンパイラのオプティマイゼーション情報、ネイティブライブラリや直接バッファなども影響するが、スレッド数が多い場合、スレッドスタックの影響は特に注目すべきポイント。
graph TD A[JVM メモリ] B[ヒープ] C[ノンヒープ] D[スレッドスタック] E[メタスペース] F[コードキャッシュ] G[Young領域] H[Old領域] I[Eden領域] J[Survivor領域] K[Permanent世代領域 -Java8未満のバージョンで使用される領域] A --> B A --> C A --> D C --> E C --> F B --> G B --> H G --> I G --> J C --> K
1. ヒープ
-
ヤング領域:
- エデン領域 (Eden Space): 新しく生成されたオブジェクトが多く配置される領域。
- サバイバー領域 (Survivor Space): オブジェクトが初めて生き残る場所。通常、S0とS1の2つの領域に分割される。
-
オールド領域 (またはテナー領域): 長期間生き残ったオブジェクトが移動する領域。
デフォルトのヒープサイズ:
- 初期ヒープサイズ (
-Xms
): 物理メモリの1/64 - 最大ヒープサイズ (
-Xmx
): 物理メモリの1/4 またはG1 GC使用時は最大25%
- 初期ヒープサイズ (
2. ノンヒープ
- メタスペース (Metaspace): JVMのメタデータやクラス定義情報が格納される領域。デフォルトの最大サイズは無制限だが、
-XX:MetaspaceSize
オプションで初期サイズを設定可能。 - コードキャッシュ: JITコンパイラによって生成されたネイティブコードの領域。
3. スレッドスタック
- 各スレッドが持つメモリ領域で、メソッドの呼び出しやローカル変数などの情報が格納される。
- デフォルトのサイズはOSやアーキテクチャにより異なるが、
-Xss
オプションで指定可能 (例:-Xss1m
で1MBを指定)。
- デフォルトのサイズはOSやアーキテクチャにより異なるが、
JVMメモリ関連の参考リンク
-
Oracle公式ドキュメント: Javaのメモリとガベージコレクションに関する基本的な情報を提供しています。
-
Java HotSpot VM オプション:
-
JVMのヒープとスタックの詳細な説明:
-
メタスペースに関する情報: