Jvm-內存申請過程分析
前置了解知識:http://wangxinchun.iteye.com/blog/2189321
內存申請過程
1、JVM會試圖為相關Java對象在Eden中初始化一塊內存區域;
2、當Eden空間足夠時,內存申請結束。否則到下一步;
3、JVM試圖釋放在Eden中所有不活躍的對象(minor collection),釋放后若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區;
4、Survivor區被用來作為Eden及old的中間交換區域,如果Survivor不足以放置eden區的對象,如果old區有空閑,那么直接放置在old區,Survivor區的對象會被移到Old區
5、當old區空間不夠時,JVM會在old區進行major collection;
完全垃圾收集后,若Survivor及old區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現"Out of memory錯誤";
1、jvm優先分配在eden區
2、當Eden空間足夠時,內存申請結束。
證明:
jvm參數設置:
-Xmx20M -Xms20M -Xmn10M -verbose:gc -XX:SurvivorRatio=8 -XX:+PrintGCDetails
其中:
-Xmx20M : 堆最大內存是20M
-Xms20M: 初始化也是20M
-Xmn10M :新生代10M,老年代10M
-verbose:gc 輸出退出后的日志
-XX:SurvivorRatio=8:eden 為8M s0 s1 各1M
-XX:+PrintGCDetails:打印gc日志的詳情
代碼:
public class TestEden { public static void main(String[] args) { byte[] b1 = new byte[1024*1024*2]; } }
日志輸出:
Heap def new generation total 9216K, used 2704K [0x03ac0000, 0x044c0000, 0x044c0000) eden space 8192K, 33% used [0x03ac0000, 0x03d64230, 0x042c0000) from space 1024K, 0% used [0x042c0000, 0x042c0000, 0x043c0000) to space 1024K, 0% used [0x043c0000, 0x043c0000, 0x044c0000) tenured generation total 10240K, used 0K [0x044c0000, 0x04ec0000, 0x04ec0000) the space 10240K, 0% used [0x044c0000, 0x044c0000, 0x044c0200, 0x04ec0000) compacting perm gen total 12288K, used 2143K [0x04ec0000, 0x05ac0000, 0x08ec0000) the space 12288K, 17% used [0x04ec0000, 0x050d7fe0, 0x050d8000, 0x05ac0000) No shared spaces configured.
通過分析main方法中申請2m的內存,內存分配到了eden區。from to tenered區都是沒有被使用。
3、JVM試圖釋放在Eden中所有不活躍的對象(minor collection),釋放后若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區;
證明:
jvm參數設置同上
代碼:
public class TestEden { public static void main(String[] args) { byte[] b1 = new byte[1024*1024*9/10]; byte[] b2 = new byte[1024*1024*8*9/10]; } }
日志:
[GC [DefNew: 1250K->1024K(9216K), 0.0015682 secs] 1250K->1062K(19456K), 0.0016006 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 9216K, used 8560K [0x26ea0000, 0x278a0000, 0x278a0000) eden space 8192K, 92% used [0x26ea0000, 0x275fc308, 0x276a0000) from space 1024K, 100% used [0x277a0000, 0x278a0000, 0x278a0000) to space 1024K, 0% used [0x276a0000, 0x276a0000, 0x277a0000) tenured generation total 10240K, used 38K [0x278a0000, 0x282a0000, 0x282a0000) the space 10240K, 0% used [0x278a0000, 0x278a9920, 0x278a9a00, 0x282a0000) compacting perm gen total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000) the space 12288K, 2% used [0x282a0000, 0x282fba28, 0x282fbc00, 0x28ea0000) ro space 8192K, 67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000) rw space 12288K, 53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)
分析:
程序執行邏輯 先申請了1M的內存,然后再次申請8M的內存,考慮內存本身結構會占用內存空間為了避免邊界都以9/10的比例來申請。
結果:
先分配了解決1M的內存在eden區,因為再次申請接近8M的內存時,eden區不夠,發生了一次young gc,1M的內存分配到了from區,接近8M的內存放在了eden區。
[GC [DefNew: 1250K->1024K(9216K), 0.0015682 secs] 1250K->1062K(19456K), 0.0016006 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
其中
9216K 為eden+from = 8M+1M=9M
19456K 為Xmx-to = 20M-1M = 19M
1250K->1024K :本次young gc 年輕代內存的變化
1250K->1062K :本次young gc 總堆內存的變化
4、Survivor區被用來作為Eden及old的中間交換區域,如果Survivor不足以放置eden區的對象,如果old區有空閑,那么直接放置在old區,Survivor區的對象會被移到Old區
證明:
jvm參數設置同上
程序如下:
public class TestEden { public static void main(String[] args) { byte[] b1 = new byte[1024*1024*5]; byte[] b2 = new byte[1024*1024*5]; System.out.println(b1+""+b2); } }
日志輸出:
[GC [DefNew: 5448K->140K(9216K), 0.0051311 secs] 5448K->5260K(19456K), 0.0051644 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [B@c17164[B@1fb8ee3 Heap def new generation total 9216K, used 5511K [0x26ea0000, 0x278a0000, 0x278a0000) eden space 8192K, 65% used [0x26ea0000, 0x273deb58, 0x276a0000) from space 1024K, 13% used [0x277a0000, 0x277c32a8, 0x278a0000) to space 1024K, 0% used [0x276a0000, 0x276a0000, 0x277a0000) tenured generation total 10240K, used 5120K [0x278a0000, 0x282a0000, 0x282a0000) the space 10240K, 50% used [0x278a0000, 0x27da0010, 0x27da0200, 0x282a0000) compacting perm gen total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000) the space 12288K, 2% used [0x282a0000, 0x282fbb28, 0x282fbc00, 0x28ea0000) ro space 8192K, 67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000) rw space 12288K, 53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)
分析:首先程序申請了5M的內存,放在了eden區,然后再次申請5M的內存,eden區不夠,發生young gc,5M放置在from區,依然不夠,故直接放置old 區
結果:eden區 819265/100/1024=5M
old區:1024050/100/1024=5M
5、如果eden區不夠,from區也承載不了,恰old區也已承載不了,那么會full gc
證明:
jvm參數配置同上
java代碼:
public class TestEden { public static void main(String[] args) { byte[] b1 = new byte[1024*1024*4]; byte[] b2 = new byte[1024*1024*5]; b1=null; b2=null; byte[] b3 = new byte[1024*1024*8]; } }
日志輸出:
[GC [DefNew: 4424K->140K(9216K), 0.0056340 secs] 4424K->4236K(19456K), 0.0056837 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [GC [DefNew: 5260K->140K(9216K), 0.0008662 secs][Tenured: 4096K->140K(10240K), 0.0084034 secs] 9356K->140K(19456K), [Perm : 366K->366K(12288K)], 0.0093567 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap def new generation total 9216K, used 163K [0x26ea0000, 0x278a0000, 0x278a0000) eden space 8192K, 2% used [0x26ea0000, 0x26ec8fc8, 0x276a0000) from space 1024K, 0% used [0x276a0000, 0x276a0000, 0x277a0000) to space 1024K, 0% used [0x277a0000, 0x277a0000, 0x278a0000) tenured generation total 10240K, used 8332K [0x278a0000, 0x282a0000, 0x282a0000) the space 10240K, 81% used [0x278a0000, 0x280c3228, 0x280c3400, 0x282a0000) compacting perm gen total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000) the space 12288K, 2% used [0x282a0000, 0x282fba68, 0x282fbc00, 0x28ea0000) ro space 8192K, 67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000) rw space 12288K, 53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)
分析:
1、先申請了4M內存,放在eden區。
2、然后申請5M內存時,發生了young gc,無奈4M被安置在了old區,5M放在了eden區
3、再次向eden區申請8M的時候,eden區不夠,同時8M還是放不下eden區,于是要full gc,full gc前先進行了young gc,5M 因為沒有引用被清理,8M放在old區時,old區滿,故full gc 4M也被清除old區。
結果:10240K, 81% used 為8M放在了old區
如果old區不夠,會發生錯誤 java.lang.OutOfMemoryError: Java heap space
public class TestEden { public static void main(String[] args) { byte[] b1 = new byte[1024*1024*4]; byte[] b2 = new byte[1024*1024*5]; b2=null; byte[] b3 = new byte[1024*1024*8]; } }
日志:
[GC [DefNew: 4424K->140K(9216K), 0.0057531 secs] 4424K->4236K(19456K), 0.0058056 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC [DefNew: 5260K->140K(9216K), 0.0008379 secs][Tenured: 4096K->4236K(10240K), 0.0092434 secs] 9356K->4236K(19456K), [Perm : 366K->366K(12288K)], 0.0101605 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [Full GC [Tenured: 4236K->4233K(10240K), 0.0075081 secs] 4236K->4233K(19456K), [Perm : 366K->365K(12288K)], 0.0075750 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at test.test.TestEden.main(TestEden.java:8) Heap def new generation total 9216K, used 251K [0x26ea0000, 0x278a0000, 0x278a0000) eden space 8192K, 3% used [0x26ea0000, 0x26edefd0, 0x276a0000) from space 1024K, 0% used [0x276a0000, 0x276a0000, 0x277a0000) to space 1024K, 0% used [0x277a0000, 0x277a0000, 0x278a0000) tenured generation total 10240K, used 4233K [0x278a0000, 0x282a0000, 0x282a0000) the space 10240K, 41% used [0x278a0000, 0x27cc2630, 0x27cc2800, 0x282a0000) compacting perm gen total 12288K, used 365K [0x282a0000, 0x28ea0000, 0x2c2a0000) the space 12288K, 2% used [0x282a0000, 0x282fb688, 0x282fb800, 0x28ea0000) ro space 8192K, 67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000) rw space 12288K, 53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)
分析:
4M 先是放在eden區,5M申請時gc 4M轉入old區,5M留在了eden區,8M申請時,發生gc5M在eden區被清理,因為eden區不夠8M,所以分配到old區,old區發生full gc,full gc依然騰不出空間,報錯。
注意:5M是在eden區gc時被清理,理由:[GC [DefNew: 5260K->140K(9216K), 0.0008379 secs][Tenured: 4096K->4236K(10240K), 0.0092434 secs] 9356K->4236K(19456K), [Perm : 366K->366K(12288K)], 0.0101605 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 日志顯示(Tenured: 4096K->4236K(10240K))無變化,但是eden區和堆總內存變化(DefNew: 5260K->140K(9216K), 9356K->4236K(19456K));
來自:http://wangxinchun.iteye.com/blog/2190330
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!