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的內存大小。  

相關文章
相關標籤/搜索