[Java] HotSpot JVM throwing OOM even when there is memory available

https://blogs.oracle.com/poonam/hotspot-jvm-throwing-oom-even-when-there-is-memory-available-v2

「まだ利用可能なメモリはたんまりあるのに、「Javaアプリケーションでヒープを使い果たすのだけど。」という質問を最近もらいました。質問の詳細は、 「-Xmx1600m を指定してアプリケーションを実行しているんだけれども、ログには1600mbを完全に使っていないのにメモリを使い果たしている、と出ている」というものです。
2017-03-21T13:15:39.478+0000: 289274.599: [Full GC [PSYoungGen: 338944K->0K(425472K)] [ParOldGen: 1092073K->1055276K(1092096K)] 1431017K->1055276K(1517568K) [PSPermGen: 493920K->493889K(494592K)], 1.1709840 secs] [Times: user=5.06 sys=0.00, real=1.18 secs]
...
2017-03-21T13:19:50.517+0000: 289525.638: [Full GC [PSYoungGen: 322035K->0K(364544K)] [ParOldGen: 1092088K->1091814K(1092096K)] 1414124K->1091814K(1456640K) [PSPermGen: 494764K->494163K(495104K)], 2.5423990 secs] [Times: user=15.30 sys=0.00, real=2.54 secs]
ヒープは最大で1517568Kにまで達することがあったり、1456640Kでとどまる時もあります。なぜ`1600mb全てを使わないのでしょうか。
そうですね、端的に言うと、説明されていない領域がSurvivor領域の一つにあるということです。
説明を加えた答えは、Young世代はEdenと2個のSurvivor領域、FromとToから構成されています。これらのうち、EdenとFrom領域からのみメモリ割り当てが可能です。Toは生存オブジェクトをコピーするために使えるよう余裕を残してあって、Young世代のキャパシティをレポートする場合に省略されます。

では別の例を見てみましょう。ここでは、たくさんの領域を割当て、Javaのヒープを使い果たすようなシンプルなプログラムを実行します。以下のオプションを付けてこのプログラムを実行します。
java -XX:+PrintGCDetails -Xmx60m -XX:MaxNewSize=20m TestProgram
......
[Full GC (Ergonomics) [PSYoungGen: 15360K->15360K(17920K)] [ParOldGen: 40464K->40464K(40960K)] 55824K->55824K(58880K), [Metaspace: 2723K->2723K(1056768K)], 0.1519409 secs] [Times: user=0.50 sys=0.00, real=0.15 secs]
[Full GC (Ergonomics) [PSYoungGen: 15360K->15360K(17920K)] [ParOldGen: 40465K->40465K(40960K)] 55825K->55825K(58880K), [Metaspace: 2723K->2723K(1056768K)], 0.1196922 secs] [Times: user=0.41 sys=0.00, real=0.12 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
        at TestProgram.main(TestProgram.java:15)[Full GC (Ergonomics) [PSYoungGen: 15360K->0K(17920K)] [ParOldGen: 40468K->324K(30720K)] 55828K->324K(48640K), [Metaspace: 2748K->2748K(1056768K)], 0.0072977 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
ヒープサイズを60MBとして構成しましたが、トータルのJavaヒープのキャパシティは、上記ログを見ると58880Kしかありません(YoungGen:17920K + OldGen:40960K = Total:58880K)。60*1024K-58880K = 2560K分はどこにいったのでしょう。

では、ヒープ利用の詳細を見てみましょう。
Heap
 PSYoungGen      total 17920K, used 307K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000)
  eden space 15360K, 2% used [0x00000000fec00000,0x00000000fec4ce70,0x00000000ffb00000)
  from space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
  to   space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
 ParOldGen       total 30720K, used 324K [0x00000000fc400000, 0x00000000fe200000, 0x00000000fec00000)
  object space 30720K, 1% used [0x00000000fc400000,0x00000000fc4511e0,0x00000000fe200000)
 Metaspace       used 2755K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 301K, capacity 386K, committed 512K, reserved 1048576K 
Capacity of PSYoungGen:17920K = eden:15360K + from:2560K
ToのSurvivor領域が含まれていないことがわかりますね。

0 件のコメント:

コメントを投稿