深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理,从此告别 JVM 内存分配文盲
點(diǎn)擊上方?好好學(xué)java?,選擇?星標(biāo)?公眾號
重磅資訊、干貨,第一時間送達(dá) 今日推薦:打卡活動第二期來啦,100% 能獲得獎品個人原創(chuàng)+1博客:點(diǎn)擊前往,查看更多虛擬機(jī)系列文章
-
深入理解 Java 虛擬機(jī)(第一彈) - Java 內(nèi)存區(qū)域透徹分析
-
深入理解 Java 虛擬機(jī)(第二彈) - 常用 vm 參數(shù)分析
-
深入理解 Java 虛擬機(jī)-如何利用 VisualVM 對高并發(fā)項(xiàng)目進(jìn)行性能分析
在前面的一篇文章深入理解Java虛擬機(jī)-如何利用VisualVM進(jìn)行性能分析中講到了一些關(guān)于JVM調(diào)優(yōu)的知識,但是,其實(shí),還是有一些問題沒有非常清楚的可以回答的,這里先給出幾個問題,然后,我們再展開這篇文章需要講解的知識。
-
我們生成的對象最開始在哪分配?Eden?Survivor?還是老年代呢?
-
進(jìn)入到老年代需要滿足什么條件呢?
接下來,我們就帶著這兩個問題展開全文。
1 對象優(yōu)先在哪分配
其實(shí),通過前面幾篇文章的講解,這個問題其實(shí)已經(jīng)見怪不怪了,在大多數(shù)的情況下,對象都是在新生代Eden區(qū)分配的,在前面的文章我們提到,在Eden區(qū)中如果內(nèi)存不夠分配的話,就會進(jìn)行一次Minor GC。同時,我們還知道年輕代中默認(rèn)下Eden:Survivor0:Survivor2 = 8:1:1,同時,還能通過參數(shù)-XX:SurvivorRatio來設(shè)置這個比例(關(guān)于這些參數(shù)的分析都可以查看這篇文章:深入理解Java虛擬機(jī)-常用vm參數(shù)分析)。
下面我們通過一個例子來分析是不是這樣的。
1.1 實(shí)例
給定JVM參數(shù):-Xms40M -Xmx40M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=4
前面三個參數(shù)設(shè)置Java堆的大小為40M,新生代為10M,緊跟著后面兩個是用于輸入GC信息。更多參數(shù)可以查看這篇文章:深入理解Java虛擬機(jī)-常用vm參數(shù)分析。
1/**2?*?@ClassName?Test_013?*?@Description?參數(shù):-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:+UseSerialGC -XX:SurvivorRatio=84?*?@Author?歐陽思海5?*?@Date?2019/12/3?16:006?*?@Version?1.07?**/8public?class?Test_01?{9 10????private?static?final?int?M?=?1024?*?1024; 11 12????public?static?void?test()?{ 13????????byte[]?alloc1,?alloc2,?alloc3,?alloc4; 14????????alloc1?=?new?byte[5?*?M]; 15????????alloc2?=?new?byte[5?*?M]; 16????????alloc3?=?new?byte[5?*?M]; 17????????alloc4?=?new?byte[10?*?M]; 18 19????} 20 21????public?static?void?main(String[]?args)?{ 22????????test(); 23????} 24 25}輸入結(jié)果:
分析
-
eden:from:to=8:1:1,這個因?yàn)榍懊嬖O(shè)置了參數(shù)-XX:SurvivorRatio=8。
-
新生代分配了20M的內(nèi)存,所以前面三個byte數(shù)組可以分配,但是,分配第四個的時候,空間不夠,所以,需要進(jìn)行一次Minor GC,GC之后,新生代從12534K變?yōu)?98K。
-
前面在新生代分配的內(nèi)存Minor GC之后,進(jìn)入到了Survivor,但是,Survivor不夠分配,所以進(jìn)入到了老年代,老年代已用內(nèi)存達(dá)到了50%。
1.2 回答問題
所以,經(jīng)過上面的例子我們發(fā)現(xiàn),對象一般優(yōu)先在新生代分配的,如果新生代內(nèi)存不夠,就進(jìn)行Minor GC回收內(nèi)存。
2 進(jìn)入到老年代需要滿足什么條件
先給出答案,分為幾點(diǎn)。
-
條件①:大對象直接進(jìn)入到老年代
-
條件②:長期存活的對象可以進(jìn)入到老年代
-
條件③:如果在Survivor空間中相同年齡所有對象的大小的總和大于Survivor空間的一半,年齡大于等于該年齡的對象直接進(jìn)入到老年代
2.1 分析條件①
-
哪些屬于大對象呢?
一般來說大對象指的是很長的字符串及數(shù)組,或者靜態(tài)對象。
-
那么需要滿足多大才是大對象呢?
這個虛擬機(jī)提供了一個參數(shù)-XX:PretenureSizeThreshold=n,只需要大于這個參數(shù)所設(shè)置的值,就可以直接進(jìn)入到老年代。
step1: 解決了這兩個問題,首先,我們不設(shè)置上面的參數(shù)的例子,將對象的內(nèi)存大于Eden的大小看看情況。
1/**2?*?@ClassName?Test_013?*?@Description?參數(shù):-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC4?*?@Author?歐陽思海5?*?@Date?2019/12/3?16:006?*?@Version?1.07?**/8public?class?Test_01?{9 10????private?static?final?int?M?=?1024?*?1024; 11 12????public?static?void?test()?{ 13????????byte[]?alloc1,?alloc2,?alloc3,?alloc4; 14//????????alloc1?=?new?byte[5?*?M]; 15//????????alloc2?=?new?byte[5?*?M]; 16//????????alloc3?=?new?byte[5?*?M]; 17????????alloc4?=?new?byte[22?*?M]; 18 19????} 20 21????public?static?void?main(String[]?args)?{ 22????????test(); 23????} 24 25}我們發(fā)現(xiàn)分配失敗,Java堆溢出,因?yàn)槌^了最大值。
step2: 下面我們看一個例子:設(shè)置-XX:PretenureSizeThreshold=104,857,600,這個單位是B字節(jié)(Byte/bait),所以這里是100M。
1/**2?*?@ClassName?Test_013?*?@Description?參數(shù):-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:PretenureSizeThreshold=104,857,6004?*?@Author?歐陽思海5?*?@Date?2019/12/3?16:006?*?@Version?1.07?**/8public?class?Test_01?{9 10????private?static?final?int?M?=?1024?*?1024; 11 12????public?static?void?test()?{ 13????????byte[]?alloc1,?alloc2,?alloc3,?alloc4; 14//????????alloc1?=?new?byte[5?*?M]; 15//????????alloc2?=?new?byte[5?*?M]; 16//????????alloc3?=?new?byte[5?*?M]; 17????????alloc4?=?new?byte[500?*?M]; 18 19????} 20 21????public?static?void?main(String[]?args)?{ 22????????test(); 23????} 24 25}發(fā)現(xiàn)新生代沒有分配,直接在老年代分配。
注意: 參數(shù)PretenureSizeThreshold只對Serial和ParNew兩款收集器有效。
2.2 分析條件②
進(jìn)入老年代規(guī)則:這里需要知道虛擬機(jī)對每個對象有個對象年齡計(jì)數(shù)器,如果對象在Eden出生經(jīng)過第一次Minor GC后任然存活,并且能夠被Survivor容納,將被移動到Survivor空間中,并且年齡設(shè)置為1。接下來,對象在Survivor中每次經(jīng)過一次Minor GC,年齡就增加1,默認(rèn)當(dāng)年齡達(dá)到15,就會進(jìn)入到老年代。
晉升到老年代的年齡閾值,可以通過參數(shù)-XX:MaxTenuringThreshold設(shè)置。
在下面的實(shí)例中,我們設(shè)置-XX:MaxTenuringThreshold=1。
1/**2?*?@ClassName?Test_013?*?@Description?參數(shù):-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:MaxTenuringThreshold=14?*?@Author?歐陽思海5?*?@Date?2019/12/3?16:006?*?@Version?1.07?**/8public?class?Test_01?{9 10????private?static?final?int?M?=?1024?*?1024; 11 12????public?static?void?test()?{ 13????????byte[]?alloc1,?alloc2,?alloc3,?alloc4; 14????????alloc1?=?new?byte[300?*?M]; 15????????alloc2?=?new?byte[300?*?M]; 16????????alloc3?=?new?byte[300?*?M]; 17????????alloc4?=?new?byte[500?*?M]; 18 19????} 20 21????public?static?void?main(String[]?args)?{ 22????????test(); 23????} 24 25}從結(jié)果可以看出,from和to都沒有占用內(nèi)存,而老年代則占用了很多內(nèi)存。
2.3 分析條件③
條件③是:如果在Survivor空間中相同年齡所有對象的大小的總和大于Survivor空間的一半,年齡大于等于該年齡的對象直接進(jìn)入到老年代,而不需要等到參數(shù)-XX:MaxTenuringThreshold設(shè)置的年齡。
實(shí)例分析
1/**2?*?@ClassName?Test_013?*?@Description?參數(shù):-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC4?*?@Author?歐陽思海5?*?@Date?2019/12/3?16:006?*?@Version?1.07?**/8public?class?Test_01?{9 10????private?static?final?int?M?=?1024?*?1024; 11 12????public?static?void?test()?{ 13????????byte[]?alloc1,?alloc2,?alloc3,?alloc4; 14????????alloc1?=?new?byte[100?*?M]; 15????????alloc2?=?new?byte[100?*?M]; 16????????//分配alloc3之前,空間不夠,所以minor GC,接著分配alloc3=900M大于Survivor空間一半,直接到老年代。 17????????alloc3?=?new?byte[900?*?M]; 18 19//????????alloc4?=?new?byte[500?*?M]; 20 21????} 22 23????public?static?void?main(String[]?args)?{ 24????????test(); 25????} 26 27}輸入結(jié)果:
分配alloc3之前,空間不夠,所以minor GC,接著分配alloc3=900M大于Survivor空間一半,直接到老年代。從而發(fā)現(xiàn),survivor占用0,而老年代占用900M。
3 總結(jié)
這篇文章主要講解了JVM內(nèi)存分配與回收策略的原理,回答了下面的這兩個問題。
-
我們生成的對象最開始在哪分配?Eden?Survivor?還是老年代呢?
-
進(jìn)入到老年代需要滿足什么條件呢?
總結(jié)
以上是生活随笔為你收集整理的深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理,从此告别 JVM 内存分配文盲的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解Java虚拟机-常用vm参数分析
- 下一篇: java美元兑换,(Java实现) 美元