JVM 调优和垃圾回收器说明
轉(zhuǎn)載自? ?JVM 調(diào)優(yōu)和垃圾回收器說明
JVM垃圾收集算法
? ? ? JVM垃圾收集算法有四種:標記-清除算法、復制算法、標記-整理算法、分代收集算法
? ? ??標記-清除算法:
? ? ? 該算法如同它的名字一樣,分為兩個階段:標記、清除。首先標記出所有需要回收的對象,然后,統(tǒng)一清除這些被標記的對象。該算法的缺點是:1、效率不高;2、產(chǎn)生大量不連續(xù)的內(nèi)存碎片,導致有大量內(nèi)存剩余的情況下,由于,沒有連續(xù)的空間來存放較大的對象,從而觸發(fā)了另一次垃圾收集動作。
? ? ? 復制算法:
? ? ? 由于標記-清除算法的效率不高,從而提出了復制算法。復制算法將可用的內(nèi)存分成兩樣大小的兩塊,每次只使用其中一塊內(nèi)存。當這塊內(nèi)存用完之后,就把還存活的對象復制到另外一塊上面,然后,把這塊清空。復制算法克服了標記-清除算法的兩個缺點,但是太浪費內(nèi)存,相當于內(nèi)存空間減小了一半。
? ? ? 隨著時間的積累,現(xiàn)在使用的復制算法的虛擬機,不再是把內(nèi)存分為1:1的兩塊。因為98%的對象是壽命很短的,創(chuàng)建之后,很快就被回收了,存活下來的只有2%,所以,用來存儲存活對象的內(nèi)存區(qū),可以小一些。現(xiàn)在的商業(yè)虛擬機是把可用內(nèi)存分為一個較大的Eden空間和兩個較小的Survivor空間,每次使用Eden和其中的一塊Survivor。當回收時,把Eden和Survivor中的存活對象一次復制到另一塊Survivor內(nèi)存區(qū)上,然后把Eden和剛才用過的Survivor空間清空。HotSpot虛擬機默認Eden和Survivor的大小比例是8:1,這樣,每次新產(chǎn)生的對象可以使用90%的內(nèi)存空間。
? ? ? 標記-整理算法
? ? ? 從名字可以看出,該算法是對“標記-清除”算法的改進升級版。同樣的該算法分為兩個階段:標記、整理。標記階段同“標記-清除”算法。整理階段,不是直接對標記對象進行清理,而是讓所有存活的對象都移動到一端,然后,直接把邊界以外的內(nèi)存清空。這就解決了“標記-清除”算法會造成大量不連續(xù)內(nèi)存碎片的問題。
? ? ? 分代收集算法
? ? ? 分代收集算法是根據(jù)對象的存活周期的不同,將內(nèi)存劃分為幾塊。當前的商業(yè)虛擬機的垃圾收集都采用了該算法。一般把Java堆分成新生代(年輕代)和老年代(年老代)。這樣就可以根據(jù)各年代中對象的存活周期來選擇最合適的收集算法了。新生代,由于只有少量的對象能存活下來,所以選用“復制算法”,只需要付出少量存活對象的復制成本。老年代,由于對象的存活率高,沒有額外的空間分擔,就必須使用“標記-清除”或“標記-整理”算法。
?
JVM垃圾收集器
? ? ? 由于內(nèi)存中的對象,是按存活周期存放在不同的內(nèi)存塊中的,所以,我們選擇不同的算法來針對不同的內(nèi)存塊進行垃圾收集。從而,對于,不同的內(nèi)存塊,我們需要有不同的垃圾收集器。
? ? ? 新生代的垃圾收集器有:Serial收集器、ParNew收集器、Parallel Scavenge收集器
? ? ? 老年代的垃圾收集器有:Serial Old收集器、Parallel Old收集器、CMS收集器、G1收集器
? ? ??
? ? ? 下面我們來分別介紹一下這些垃圾收集器
? ? ? Serial收集器/Serial Old收集器
? ? ? Serial收集器/Serial Old收集器,是單線程的,使用“復制”算法。當它工作時,必須暫停其它所有工作線程。特點:簡單而高效。對于運行在Client模式下的虛擬機來說是一個很好的選擇。
? ? ? ParNew收集器
? ? ? ParNew收集器,是Serial收集器的多線程版。是運行在Server模式下的虛擬機中首選的新生代收集器。除了Serial收集器外,目前只有它能與CMS收集器配合工作。
? ? ? Parallel Scavenge收集器/Parallel Old收集器
? ? ? Parallel Scavenge收集器,也是使用“復制”算法的、并行的多線程收集器。這些都和ParNew收集器一樣。但它關(guān)注的是吞吐量(CPU用于運行用戶代碼的時間與CPU總消耗時間的比值),而其它收集器(Serial/Serial Old、ParNew、CMS)關(guān)注的是垃圾收集時用戶線程的停頓時間。
? ? ? Parallel Old收集器是Parallel Scavenge收集器的老年代版本。
? ? ? CMS收集器
? ? ? CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器,使用“標記-清除”算法。
? ? ? CMS收集器分4個步驟進行垃圾收集工作:
? ? ? 1、初始標記 ? 2、并發(fā)標記 ? 3、重新標記 ? 4、并發(fā)清除
? ? ? 其中“初始標記”、“重新標記”是需要暫停其它所有工作線程的。
? ? ? G1收集器
? ? ? G1(Garbage First)收集器,基于“標記-整理”算法,可以非常精確地控制停頓。
問題
當我們的代碼出現(xiàn)下面的情形時,該算法將無法適應
a)?????????ObjA.obj = ObjB
b)?????????ObjB.obj - ObjA
?????????????????這樣的代碼會產(chǎn)生如下引用情形?objA指向objB,而objB又指向objA,這樣當其他所有的引用都消失了之后,objA和objB還有一個相互的引用,也就是說兩個對象的引用計數(shù)器各為1,而實際上這兩個對象都已經(jīng)沒有額外的引用,已經(jīng)是垃圾了。
? ? ? ? ? ? ? ?
?
2、??????????????根搜索算法
???????????????????根搜索算法是從離散數(shù)學中的圖論引入的,程序把所有的引用關(guān)系看作一張圖,從一個節(jié)點GC ROOT開始,尋找對應的引用節(jié)點,找到這個節(jié)點以后,繼續(xù)尋找這個節(jié)點的引用節(jié)點,當所有的引用節(jié)點尋找完畢之后,剩余的節(jié)點則被認為是沒有被引用到的節(jié)點,即無用的節(jié)點。
?
?
?
目前java中可作為GC Root的對象有
1、????虛擬機棧中引用的對象(本地變量表)
2、????方法區(qū)中靜態(tài)屬性引用的對象
3、????方法區(qū)中常量引用的對象
4、????本地方法棧中引用的對象(Native對象)
說了這么多,其實我們可以看到,所有的垃圾回收機制都是和引用相關(guān)的,那我們來具體的來看一下引用的分類,到底有哪些類型的引用?每種引用都是做什么的呢?
Java中存在四種引用,每種引用如下:
1、??強引用
只要引用存在,垃圾回收器永遠不會回收
Object obj = new Object();
//可直接通過obj取得對應的對象?如obj.equels(new Object());
而這樣?obj對象對后面new Object的一個強引用,只有當obj這個引用被釋放之后,對象才會被釋放掉,這也是我們經(jīng)常所用到的編碼形式。
2、??軟引用
非必須引用,內(nèi)存溢出之前進行回收,可以通過以下代碼實現(xiàn)
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有時候會返回null
這時候sf是對obj的一個軟引用,通過sf.get()方法可以取到這個對象,當然,當這個對象被標記為需要回收的對象時,則返回null;
軟引用主要用戶實現(xiàn)類似緩存的功能,在內(nèi)存足夠的情況下直接通過軟引用取值,無需從繁忙的真實來源查詢數(shù)據(jù),提升速度;當內(nèi)存不足時,自動刪除這部分緩存數(shù)據(jù),從真正的來源查詢這些數(shù)據(jù)。
3、??弱引用
第二次垃圾回收時回收,可以通過如下代碼實現(xiàn)
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有時候會返回null
wf.isEnQueued();//返回是否被垃圾回收器標記為即將回收的垃圾
弱引用是在第二次垃圾回收時回收,短時間內(nèi)通過弱引用取對應的數(shù)據(jù),可以取到,當執(zhí)行過第二次垃圾回收時,將返回null。
弱引用主要用于監(jiān)控對象是否已經(jīng)被垃圾回收器標記為即將回收的垃圾,可以通過弱引用的isEnQueued方法返回對象是否被垃圾回收器
4、??虛引用(幽靈/幻影引用)
???????????垃圾回收時回收,無法通過引用取到對象值,可以通過如下代碼實現(xiàn)
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永遠返回null
pf.isEnQueued();//返回從內(nèi)存中已經(jīng)刪除
虛引用是每次垃圾回收的時候都會被回收,通過虛引用的get方法永遠獲取到的數(shù)據(jù)為null,因此也被成為幽靈引用。
虛引用主要用于檢測對象是否已經(jīng)從內(nèi)存中刪除。
在上文中已經(jīng)提到了,我們的對象在內(nèi)存中會被劃分為5塊區(qū)域,而每塊數(shù)據(jù)的回收比例是不同的,根據(jù)IBM的統(tǒng)計,數(shù)據(jù)如下圖所示:
?
?我們知道,方法區(qū)主要存放類與類之間關(guān)系的數(shù)據(jù),而這部分數(shù)據(jù)被加載到內(nèi)存之后,基本上是不會發(fā)生變更的,
Java堆中的數(shù)據(jù)基本上是朝生夕死的,我們用完之后要馬上回收的,而Java棧和本地方法棧中的數(shù)據(jù),因為有后進先出的原則,當我取下面的數(shù)據(jù)之前,必須要把棧頂?shù)脑爻鰲?#xff0c;因此回收率可認為是100%;而程序計數(shù)器我們前面也已經(jīng)提到,主要用戶記錄線程執(zhí)行的行號等一些信息,這塊區(qū)域也是被認為是唯一一塊不會內(nèi)存溢出的區(qū)域。在SunHostSpot的虛擬機中,對于程序計數(shù)器是不回收的,而方法區(qū)的數(shù)據(jù)因為回收率非常小,而成本又比較高,一般認為是“性價比”非常差的,所以Sun自己的虛擬機HotSpot中是不回收的!但是在現(xiàn)在高性能分布式J2EE的系統(tǒng)中,我們大量用到了反射、動態(tài)代理、CGLIB、JSP和OSGI等,這些類頻繁的調(diào)用自定義類加載器,都需要動態(tài)的加載和卸載了,以保證永久帶不會溢出,他們通過自定義的類加載器進行了各項操作,因此在實際的應用開發(fā)中,類也是被經(jīng)常加載和卸載的,方法區(qū)也是會被回收的!但是方法區(qū)的回收條件非常苛刻,只有同時滿足以下三個條件才會被回收!
?
1、所有實例被回收
2、加載該類的ClassLoader被回收
3、Class對象無法通過任何途徑訪問(包括反射)
好了,我們現(xiàn)在切入正題,Java1.2之前主要通過引用計數(shù)器來標記是否需要垃圾回收,而1.2之后都使用根搜索算法來收集垃圾,而收集后的垃圾是通過什么算法來回收的呢?
1、????標記-清除算法
2、????復制算法
3、????標記-整理算法
我們來逐一過一下
1、????標記-清除算法
?
?
標記-清除算法采用從根集合進行掃描,對存活的對象對象標記,標記完畢后,再掃描整個空間中未被標記的對象,進行回收,如上圖所示。
標記-清除算法不需要進行對象的移動,并且僅對不存活的對象進行處理,在存活對象比較多的情況下極為高效,但由于標記-清除算法直接回收不存活的對象,因此會造成內(nèi)存碎片!
2、????復制算法
??
?復制算法采用從根集合掃描,并將存活對象復制到一塊新的,沒有使用過的空間中,這種算法當控件存活的對象比較少時,極為高效,但是帶來的成本是需要一塊內(nèi)存交換空間用于進行對象的移動。也就是我們前面提到的
s0 s1等空間。
?
3、????標記-整理算法
???
?標記
-整理算法采用標記-清除算法一樣的方式進行對象的標記,但在清除時不同,在回收不存活的對象占用的空間后,會將所有的存活對象往左端空閑空間移動,并更新對應的指針。標記-整理算法是在標記-清除算法的基礎(chǔ)上,又進行了對象的移動,因此成本更高,但是卻解決了內(nèi)存碎片的問題。
?
我們知道,JVM為了優(yōu)化內(nèi)存的回收,進行了分代回收的方式,對于新生代內(nèi)存的回收(minor GC)主要采用復制算法,下圖展示了minor GC的執(zhí)行過程。
?
??
?對于新生代和舊生代,
JVM可使用很多種垃圾回收器進行垃圾回收,下圖展示了不同生代不通垃圾回收器,其中兩個回收器之間有連線表示這兩個回收器可以同時使用。
?
?而這些垃圾回收器又分為串行回收方式、并行回收方式合并發(fā)回收方式執(zhí)行,分別運用于不同的場景。如下圖所示
?
?下面我們來逐一介紹一下每個垃圾回收器。
?
1、????Serial收集器
看名字我們都可以看的出來,這個屬于串行收集器。其運行示意圖如下
?Serial
收集器是歷史最悠久的一個回收器,JDK1.3之前廣泛使用這個收集器,目前也是ClientVM下?ServerVM 4核4GB以下機器的默認垃圾回收器。串行收集器并不是只能使用一個CPU進行收集,而是當JVM需要進行垃圾回收的時候,需要中斷所有的用戶線程,知道它回收結(jié)束為止,因此又號稱“Stop The World”?的垃圾回收器。注意,JVM中文名稱為java虛擬機,因此它就像一臺虛擬的電腦一樣在工作,而其中的每一個線程就被認為是JVM的一個處理器,因此大家看到圖中的CPU0、CPU1實際為用戶的線程,而不是真正機器的CPU,大家不要誤解哦。
?
串行回收方式適合低端機器,是Client模式下的默認收集器,對CPU和內(nèi)存的消耗不高,適合用戶交互比較少,后臺任務較多的系統(tǒng)。
Serial收集器默認新舊生代的回收器搭配為Serial+ SerialOld
2、????ParNew收集器
ParNew收集器其實就是多線程版本的Serial收集器,其運行示意圖如下
?同樣有
Stop The World的問題,他是多CPU模式下的首選回收器(該回收器在單CPU的環(huán)境下回收效率遠遠低于Serial收集器,所以一定要注意場景哦),也是Server模式下的默認收集器。
?
3、????ParallelScavenge
ParallelScavenge又被稱為是吞吐量優(yōu)先的收集器,器運行示意圖如下
?
?ParallelScavenge
所提到的吞吐量=程序運行時間/(JVM執(zhí)行回收的時間+程序運行時間),假設程序運行了100分鐘,JVM的垃圾回收占用1分鐘,那么吞吐量就是99%。在當今網(wǎng)絡告訴發(fā)達的今天,良好的響應速度是提升用戶體驗的一個重要指標,多核并行云計算的發(fā)展要求程序盡可能的使用CPU和內(nèi)存資源,盡快的計算出最終結(jié)果,因此在交互不多的云端,比較適合使用該回收器。
?
4、????ParallelOld
ParallelOld是老生代并行收集器的一種,使用標記整理算法、是老生代吞吐量優(yōu)先的一個收集器。這個收集器是JDK1.6之后剛引入的一款收集器,我們看之前那個圖之間的關(guān)聯(lián)關(guān)系可以看到,早期沒有ParallelOld之前,吞吐量優(yōu)先的收集器老生代只能使用串行回收收集器,大大的拖累了吞吐量優(yōu)先的性能,自從JDK1.6之后,才能真正做到較高效率的吞吐量優(yōu)先。其運行示意圖如下
???
?5、?SerialOld
SerialOld是舊生代Client模式下的默認收集器,單線程執(zhí)行;在JDK1.6之前也是ParallelScvenge回收新生代模式下舊生代的默認收集器,同時也是并發(fā)收集器CMS回收失敗后的備用收集器。其運行示意圖如下
?
?6、?CMS
CMS又稱響應時間優(yōu)先(最短回收停頓)的回收器,使用并發(fā)模式回收垃圾,使用標記-清除算法,CMS對CPU是非常敏感的,它的回收線程數(shù)=(CPU+3)/4,因此當CPU是2核的實惠,回收線程將占用的CPU資源的50%,而當CPU核心數(shù)為4時僅占用25%。他的運行示意圖如下
?
?CMS
模式主要分為4個過程
?在初始標記的時候,需要中斷所有用戶線程,在并發(fā)標記階段,用戶線程和標記線程
并發(fā)執(zhí)行,而在這個過程中,隨著內(nèi)存引用關(guān)系的變化,可能會發(fā)生原來標記的對象被釋放,進而引發(fā)新的垃圾,因此可能會產(chǎn)生一系列的浮動垃圾,不能被回收。
?
CMS?為了確保能夠掃描到所有的對象,避免在Initial Marking?中還有未標識到的對象,采用的方法為找到標記了的對象,并將這些對象放入Stack?中,掃描時尋找此對象依賴的對象,如果依賴的對象的地址在其之前,則將此對象進行標記,并同時放入Stack?中,如依賴的對象地址在其之后,則僅標記該對象。
在進行Concurrent Marking?時minor GC?也可能會同時進行,這個時候很容易造成舊生代對象引用關(guān)系改變,CMS?為了應對這樣的并發(fā)現(xiàn)象,提供了一個Mod Union Table?來進行記錄,在這個Mod Union Table中記錄每次minor GC?后修改了的Card?的信息。這也是ParallelScavenge不能和CMS一起使用的原因。
CMS產(chǎn)生浮動垃圾的情況請見如下示意圖
?
在運行回收過后,c就變成了浮動垃圾。
由于CMS會產(chǎn)生浮動垃圾,當回收過后,浮動垃圾如果產(chǎn)生過多,同時因為使用標記-清除算法會產(chǎn)生碎片,可能會導致回收過后的連續(xù)空間仍然不能容納新生代移動過來或者新創(chuàng)建的大資源,因此會導致CMS回收失敗,進而觸發(fā)另外一次FULL GC,而這時候則采用SerialOld進行二次回收。
同時CMS因為可能產(chǎn)生浮動垃圾,而CMS在執(zhí)行回收的同時新生代也有可能在進行回收操作,為了保證舊生代能夠存放新生代轉(zhuǎn)移過來的數(shù)據(jù),CMS在舊生代內(nèi)存到達全部容量的68%就觸發(fā)了CMS的回收!
7、????GarbageFirst(G1 )
我們再來看垃圾回收器的總圖,剛才我們可以看到,我在圖上標記了一個?,其實這是一個新的垃圾回收器,既可以回收新生代也可以回收舊生代,SunHotSpot 1.6u14以上EarlyAccess版本加入了這個回收器,sun公司預期SunHotSpot1.7發(fā)布正式版,他是商用高性能垃圾回收器,通過重新劃分內(nèi)存區(qū)域,整合優(yōu)化CMS,同時注重吞吐量和響應時間,但是杯具的是被oracle收購之后這個收集器屬于商用收費收集器,因此目前基本上沒有人使用,我們在這里也就不多介紹,更多信息可以參考oracle新版本JDK說明。
下面我們再來看下JVM的一些內(nèi)存分配與回收策略
1、????優(yōu)先在Edon上分配對象
代碼示例
package com.yhj.jvm.gc.edenFirst;/*** @Described:Edon優(yōu)先劃分對象測試* VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc* Edon s0 s1 old* 8 1 1 10* @author YHJ create at 2012-1-3 下午04:44:43* @FileNmae com.yhj.jvm.gc.edenFirst.EdonFirst.java*/public class EdonFirst {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4;testCase1 = new byte[2*ONE_MB];testCase2 = new byte[2*ONE_MB];testCase3 = new byte[2*ONE_MB];// testCase1 = null;// testCase2 = null;// testCase3 = null;testCase4 = new byte[2*ONE_MB];}}運行結(jié)果
?
?結(jié)果分析
從運行結(jié)果我們可以很清晰的看到,eden有8MB的存儲控件(通過參數(shù)配置),前6MB的數(shù)據(jù)優(yōu)先分配到eden區(qū)域,當下一個2MB存放時,因空間已滿,觸發(fā)一次GC,但是這部分數(shù)據(jù)因為沒有回收(引用還在,當賦值為null后則不會轉(zhuǎn)移),數(shù)據(jù)會被復制到s0區(qū)域,但是s0區(qū)域不夠存儲,因此直接放入老生代區(qū)域,新的2MB數(shù)據(jù)存放在eden區(qū)域
2、????大對象直接進入老生代
代碼示例
運行結(jié)果
?結(jié)果分析
從代碼中我們可以看到,當testCase1劃分為0.25MB數(shù)據(jù),進行多次大對象創(chuàng)建之后,testCase1應該在GC執(zhí)行之后被復制到s0區(qū)域(s0足以容納testCase1),但是我們設置了對象的年齡為1,即超過1歲便進入老生代,因此GC執(zhí)行2次后testCase1直接被復制到了老生代,而默認進入老生代的年齡為15。我們通過profilter的監(jiān)控工具可以很清楚的看到對象的年齡,如圖所示
?
?
?右側(cè)的年代數(shù)目就是對象的年齡
?
4、????群體效應(大批中年對象進入老生代)
代碼示例
package com.yhj.jvm.gc.dynamicMoreAVG_intoOld;/*** @Described:s0占用空間到達50%直接進入老生代* VM params : -Xms20M -Xmx20M -Xmn10M -XX:MaxTenuringThreshold=15-XX:+PrintGCDetails -verbose:gc* Edon s0 s1 old age* 8 1 1 10 15* 0.5 0 0 7.5* 7.5 0.5 0 7.5* 7.5 0 0 8* @author YHJ create at 2012-1-3 下午05:50:40* @FileNmae com.yhj.jvm.gc.dynamicMoreAVG_intoOld.MoreAVG_intoOld.java*/public class MoreAVG_intoOld {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4;testCase1 = new byte[7*ONE_MB+ONE_MB/2];testCase2 = new byte[ONE_MB/2];testCase3 = new byte[7*ONE_MB+ONE_MB/2];testCase3 = null;testCase4 = new byte[7*ONE_MB+ONE_MB/2];// testCase1 = new byte[7*ONE_MB+3*ONE_MB/4];// testCase2 = new byte[ONE_MB/4];// testCase3 = new byte[7*ONE_MB+3*ONE_MB/4];}}運行結(jié)果
?
?結(jié)果分析
我們看到,當創(chuàng)建后testCase3,testCase2被移動到s0區(qū)域,當被釋放后,繼續(xù)創(chuàng)建testCase3,按理說testCase2應該移動到s1區(qū)域,但是因為超過了s1區(qū)域的1/2,因此直接進入老生代
5、????擔保GC(擔保minorGC)
擔保GC就是擔保minorGC能夠滿足當前的存儲空間,而無需觸發(fā)老生代的回收,由于大部分對象都是朝生夕死的,因此,在實際開發(fā)中這種很起效,但是也有可能會發(fā)生擔保失敗的情況,當擔保失敗的時候會觸發(fā)FullGC,但是失敗畢竟是少數(shù),因此這種一般是很劃算的。
?
代碼示例
package com.yhj.jvm.gc.securedTransactions;/*** @Described:擔保交易測試* VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc-XX:-HandlePromotionFailure 無擔保* VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc-XX:+HandlePromotionFailure 有擔保* Edon s0 s1 old * 8 1 1 10 * @author YHJ create at 2012-1-3 下午06:11:17* @FileNmaecom.yhj.jvm.gc.securedTransactions.SecuredTransactions.java*/public class SecuredTransactions {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4,testCase5,testCase6,testCase7;testCase1 = new byte[2*ONE_MB];testCase2 = new byte[2*ONE_MB];testCase3 = new byte[2*ONE_MB];testCase1 = null;testCase4 = new byte[2*ONE_MB];testCase5 = new byte[2*ONE_MB];testCase6 = new byte[2*ONE_MB];testCase4 = null;testCase5 = null;testCase6 = null;testCase7 = new byte[2*ONE_MB];}}?運行結(jié)果
1、??無擔保
? ??
?2、有擔保
?結(jié)果分析
我們可以很清楚的看到,當無擔保的時候,觸發(fā)了一次FullGC?而有擔保的情況下,只有monorGC則完成了回收,大大提升了效率。
當我們注釋掉對應的代碼
// testCase4 = null; // testCase5 = null; // testCase6 = null;?的時候,就會引發(fā)擔保失敗,如下圖所示
JVM默認情況是是開啟擔保的,無需設置參數(shù)。
總結(jié)
以上是生活随笔為你收集整理的JVM 调优和垃圾回收器说明的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java的GC机制及算法
- 下一篇: java io系列09之 FileDes