一般的な OOM 例外の分析とトラブルシューティング
Java のメモリ不足
Java ヒープは、オブジェクト インスタンスの保存に使用されます。オブジェクトが継続的に作成され、GC ルートとオブジェクトの間に到達可能なパスがある場合、ガベージ コレクション メカニズムはこれらのオブジェクトをクリーンアップしません。オブジェクトの数が最大ヒープ容量に達すると、ガベージ コレクション メカニズムはこれらのオブジェクトをクリーンアップしません。制限するとメモリが生成されます。オーバーフロー例外です。
Java ヒープ オーバーフローの原因
- Java ヒープにオブジェクトを割り当てることができません
- アプリケーションは GC で収集できないオブジェクトを保存します
- アプリケーションがファイナライザーを過剰に使用する
ソリューション
- 次のような主要なエラー メッセージを見つけます。
java.lang.StackOverflowError
java.lang.OutOfMemoryError:java heap space
java.lang.OutOfMemoryError:GC overhead limit exceeeded
java.lang.OutOfMemoryError:Direct buffer memory
java.lang.OutOfMemoryError:unable to create new native thread
java.lang.OutOfMemoryError:Metaspace
- Java プロセスの ID (PID) を確認し、jps -vl コマンドを使用して Java プロセスの PID と起動時に設定された jvm パラメーターを確認します。
jps -vl
- 新世代と旧世代のヒープ メモリの割り当てサイズと使用量を表示する
jmap -heap PID
[xxx@xxx ~]# jmap -heap 15162
Attaching to process ID 15162, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.161-b12
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40 # 最小堆使用比例
MaxHeapFreeRatio = 70 # 最大堆可用比例
MaxHeapSize = 482344960 (460.0MB) # 最大堆空间大小
NewSize = 10485760 (10.0MB) # 新生代分配大小
MaxNewSize = 160759808 (153.3125MB) # 最大新生代可分配大小
OldSize = 20971520 (20.0MB) # 老年代大小
NewRatio = 2 # 新生代比例
SurvivorRatio = 8 # 新生代与 Survivor 比例
MetaspaceSize = 21807104 (20.796875MB) # 元空间大小
CompressedClassSpaceSize = 1073741824 (1024.0MB) # Compressed Class Space 空间大小限制
MaxMetaspaceSize = 17592186044415 MB # 最大元空间大小
G1HeapRegionSize = 0 (0.0MB) # G1 单个 Region 大小
Heap Usage: # 堆使用情况
New Generation (Eden + 1 Survivor Space): # 新生代
capacity = 9502720 (9.0625MB) # 新生代总容量
used = 4995320 (4.763908386230469MB) # 新生代已使用
free = 4507400 (4.298591613769531MB) # 新生代剩余容量
52.56726495150862% used # 新生代使用占比
Eden Space:
capacity = 8454144 (8.0625MB) # Eden 区总容量
used = 4029752 (3.8430709838867188MB) # Eden 区已使用
free = 4424392 (4.219429016113281MB) # Eden 区剩余容量
47.665996699370154% used # Eden 区使用占比
From Space: # 其中一个 Survivor 区的内存分布
capacity = 1048576 (1.0MB)
used = 965568 (0.92083740234375MB)
free = 83008 (0.07916259765625MB)
92.083740234375% used
To Space: # 另一个 Survivor 区的内存分布
capacity = 1048576 (1.0MB)
used = 0 (0.0MB)
free = 1048576 (1.0MB)
0.0% used
tenured generation: # 老年代
capacity = 20971520 (20.0MB)
used = 10611384 (10.119804382324219MB)
free = 10360136 (9.880195617675781MB)
50.599021911621094% used
10730 interned Strings occupying 906232 bytes.
- 最もメモリを消費するオブジェクトをクエリすると、残ったオブジェクトの情報がテーブル形式で表示され、ランキング、インスタンス数、占有メモリ サイズ、クラス名などの情報が占有メモリ サイズに従って並べ替えられます。
jmap -histo:live PID | more
- ダンプ ファイルの分析
ダンプ ファイルは Java プロセスのメモリ イメージであり、主にシステム情報、仮想マシンのプロパティ、完全なスレッド ダンプ、すべてのクラスとオブジェクトのステータスなどが含まれます。 JVM 起動パラメータ設定に次のパラメータを追加します
。
- -XX:+HeapDumpOnOutOfMemoryエラー
- -XX:HeapDumpPath=./ (パラメータはダンプ ファイルの生成パスです)
JVM 起動パラメータ設定では、次のパラメータが追加されます。上記の
設定は、アプリケーションが OOM をスローした後にダンプを自動的にエクスポートするか、JVM 起動時にダンプ ファイルをエクスポートできます。が走っています
jmap -dump:file=[文件路径] [pid]
# 示例
jmap -dump:file=./jvmdump.hprof 15892
デモ
设置 VM 参数:-Xms3m -Xmx3m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
public static void main(String[] args) {
List<Object> oomList = Lists.newArrayList();
// 无限循环创建对象
while (true) {
oomList.add(new Object());
}
}
要約する
オンラインで JVM メモリ オーバーフローが発生した場合は、
次の手順で jmap -heap をチェックして、メモリ割り当てが小さすぎるかどうかを確認できます。
jmap -histo は、割り当てられすぎて解放されていない明らかなオブジェクトがあるかどうかを確認します
jmap -dump は、JVM の現在のメモリ スナップショットをエクスポートし、JDK や MAT などのツールを使用してスナップショットを分析します