JVM扫盲(四)

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

为何即使内存足够可用, Hotspot JVM 仍会抛出OOM ??oracle

先看下问题细节 - 以-Xmx1600M运行app, 可是日志显示在没有占用内存到1600MB的时候, 缺抛出OOM。app

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]jvm

OK, 看到上面的日志可知,堆内存最大的容量有时是1517568K 有时是1456640K。为何都没有达到1600MB呢?spa

well, 简而言之:剩余的内存空间是为幸存对象转移准备的。日志

具体说来, 年轻代由2个区域组成:  1个Eden space 和 2个Survivor space(From survivor和 To survivor)。在这3个space中仅有'Eden' space 和 'From' space 2个 space做分配使用。 'To' space 会空出来以拷贝幸存对象, 而且在显示年轻代占有内存大小时,'To' space 所占大小会被忽略不计。对象

再看另一个例子,跑的程序比较耗费内存而且致使OOM。运行这个程序的命令: java -XX:PrintGCDetails -Xmx60M -XX:MaxNewSize=20M TestApplication, 日志打印以下;
 
...<snip>...
[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]blog

[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]ip

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, 可是从上面的日志信息里能够看出最大的堆内存倒是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

PSYoungGen的容量: 17920K=eden: 15360K + from: 2560K。 很明显年轻代的内存大小不包含'To' survivor space的内存大小。  

相关文章
相关标签/搜索