久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

深入理解JVM

 好閨女瑤瑤 2016-05-04

Java語言出來之前,,大家都在拼命的寫C或者C++的程序,而此時(shí)存在一個(gè)很大的矛盾,,C++等語言創(chuàng)建對象要不斷的去開辟空間,,不用的時(shí)候有需要不斷的去釋放控件,既要寫構(gòu)造函數(shù),,又要寫析構(gòu)函數(shù),,很多時(shí)候都在重復(fù)的allocated,,然后不停的~析構(gòu)。于是,,有人就提出,,能不能寫一段程序在實(shí)現(xiàn)這塊功能,每次創(chuàng)建,,釋放控件的時(shí)候復(fù)用這段代碼,,而無需重復(fù)的書寫呢?

1960 基于MITLisp首先提出了垃圾回收的概念,,用于處理C語言等不停的析構(gòu)操作,,而這時(shí)Java還沒有出世呢!所以實(shí)際上GC并不是Java的專利,,GC的歷史遠(yuǎn)遠(yuǎn)大于Java的歷史,!

那究竟GC為我們做了什么操作呢,?

 

1,、    哪些內(nèi)存需要回收?

2,、    什么時(shí)候回收,?

3,、    如何回收?

 

這時(shí)候有人就會疑惑了,,既然GC已經(jīng)為我們解決了這個(gè)矛盾,,我們還需要學(xué)習(xí)GC么?當(dāng)然當(dāng)然是肯定的,,那究竟什么時(shí)候我們還需要用到的呢,?

 

1、    排查內(nèi)存溢出

2,、    排查內(nèi)存泄漏

3,、    性能調(diào)優(yōu),排查并發(fā)瓶頸

 

我們知道,,GC主要處理的是對象的回收操作,,那么什么時(shí)候會觸發(fā)一個(gè)對象的回收的呢?

1,、    對象沒有引用

2,、    作用域發(fā)生未捕獲異常

3、    程序在作用域正常執(zhí)行完畢

4,、    程序執(zhí)行了System.exit()

5,、    程序發(fā)生意外終止(被殺進(jìn)程等)

其實(shí),我們最容易想到的就是當(dāng)對象沒有引用的時(shí)候會將這個(gè)對象標(biāo)記為可回收對象,那么現(xiàn)在就有一個(gè)問題,,是不是這個(gè)對象被賦值為null以后就一定被標(biāo)記為可回收對象了呢,?我們來看一個(gè)例子:

 

package com.yhj.jvm.gc.objEscape.finalizeEscape;

 

import com.yhj.jvm.gc.objEscape.pojo.FinalizedEscapeTestCase;

 

 

/**

 * @Described:逃逸分析測試

 * @author YHJ create at 2011-12-24 下午05:08:09

 * @FileNmae com.yhj.jvm.gc.finalizeEscape.FinalizedEscape.java

 */

public class FinalizedEscape {

    public static void main(String[] args) throwsInterruptedException {

        System.out.println(FinalizedEscapeTestCase.caseForEscape);

       FinalizedEscapeTestCase.caseForEscape = newFinalizedEscapeTestCase();

        System.out.println(FinalizedEscapeTestCase.caseForEscape);

       FinalizedEscapeTestCase.caseForEscape=null;

       System.gc();

       Thread.sleep(100);

        System.out.println(FinalizedEscapeTestCase.caseForEscape);

    }

}

package com.yhj.jvm.gc.objEscape.pojo;

/**

 * @Described:逃逸分析測試用例

 * @author YHJ create at 2011-12-24 下午05:07:05

 * @FileNmae com.yhj.jvm.gc.pojo.TestCaseForEscape.java

 */

public class FinalizedEscapeTestCase {

 

    public static FinalizedEscapeTestCase caseForEscape = null;

    @Override

    protected void finalize() throws Throwable {

       super.finalize();

       System.out.println("哈哈,我已逃逸,!");

       caseForEscape = this;

    }

}

 

程序的運(yùn)行結(jié)果回事什么樣子的呢,?

我們來看這段代碼

 

1、  System.out.println(FinalizedEscapeTestCase.caseForEscape);

2,、  FinalizedEscapeTestCase.caseForEscape = newFinalizedEscapeTestCase();

3,、  System.out.println(FinalizedEscapeTestCase.caseForEscape);

4,、  FinalizedEscapeTestCase.caseForEscape=null;

5,、  System.gc();

6、  Thread.sleep(100);

7,、    System.out.println(FinalizedEscapeTestCase.caseForEscape);

 

1,、    當(dāng)程序執(zhí)行第一行是,因?yàn)檫@個(gè)對象沒有值,,結(jié)果肯定是null

2,、    程序第二行給該對象賦值為新開辟的一個(gè)對象

3、    第三行打印的時(shí)候,,肯定是第二行對象的hash代碼

4,、    第四行將該對象重新置為null

5、    第五行觸發(fā)GC

6,、    為了保證GC能夠順利執(zhí)行完畢,,第六行等待100毫秒

7、    第七行打印對應(yīng)的值,,回事null么,?一定會是null么?

我們來看一下對應(yīng)的運(yùn)行結(jié)果


 本例中打印了

GC的日志,,讓我們看的更清晰一點(diǎn),,我們很清晰的看出,最后一句打印的不是null,,并且子啊之前,,還出現(xiàn)了逃逸的字樣。說明這個(gè)對象逃逸了,,在垃圾回收之前逃逸了,,我們再來看這個(gè)pojo的寫法,就會發(fā)現(xiàn),,我們重寫了方法finalize,,而這個(gè)方法就相當(dāng)于C++中的析構(gòu)方法,在GC回收之前,會先調(diào)用一次這個(gè)方法,,而這個(gè)方法又將this指針指向他自己,,因此得以成功逃逸!可見,,并不是這個(gè)對象被賦值為null之后就一定被標(biāo)記為可回收,,有可能會發(fā)生逃逸!

 

下面我們來看一下幾種垃圾收集算法

1,、              JDK1.2之前,,使用的是引用計(jì)數(shù)器算法,即當(dāng)這個(gè)類被加載到內(nèi)存以后,,就會產(chǎn)生方法區(qū),,堆棧、程序計(jì)數(shù)器等一系列信息,,當(dāng)創(chuàng)建對象的時(shí)候,,為這個(gè)對象在堆棧空間中分配對象,,同時(shí)會產(chǎn)生一個(gè)引用計(jì)數(shù)器,,同時(shí)引用計(jì)數(shù)器+1,當(dāng)有新的引用的時(shí)候,,引用計(jì)數(shù)器繼續(xù)+1,,而當(dāng)其中一個(gè)引用銷毀的時(shí)候,引用計(jì)數(shù)器-1,,當(dāng)引用計(jì)數(shù)器被減為零的時(shí)候,,標(biāo)志著這個(gè)對象已經(jīng)沒有引用了,可以回收了,!這種算法在JDK1.2之前的版本被廣泛使用,,但是隨著業(yè)務(wù)的發(fā)展,很快出現(xiàn)了一個(gè)問題

當(dāng)我們的代碼出現(xiàn)下面的情形時(shí),,該算法將無法適應(yīng)

a)         ObjA.obj = ObjB

b)         ObjB.obj - ObjA

                 這樣的代碼會產(chǎn)生如下引用情形 objA指向objB,,而objB又指向objA,這樣當(dāng)其他所有的引用都消失了之后,,objAobjB還有一個(gè)相互的引用,,也就是說兩個(gè)對象的引用計(jì)數(shù)器各為1,而實(shí)際上這兩個(gè)對象都已經(jīng)沒有額外的引用,,已經(jīng)是垃圾了,。

               


 

 

2、              根搜索算法

                   根搜索算法是從離散數(shù)學(xué)中的圖論引入的,,程序把所有的引用關(guān)系看作一張圖,,從一個(gè)節(jié)點(diǎn)GC ROOT開始,,尋找對應(yīng)的引用節(jié)點(diǎn),找到這個(gè)節(jié)點(diǎn)以后,,繼續(xù)尋找這個(gè)節(jié)點(diǎn)的引用節(jié)點(diǎn),,當(dāng)所有的引用節(jié)點(diǎn)尋找完畢之后,剩余的節(jié)點(diǎn)則被認(rèn)為是沒有被引用到的節(jié)點(diǎn),,即無用的節(jié)點(diǎn),。

 



 

 

目前java中可作為GC Root的對象有

1、    虛擬機(jī)棧中引用的對象(本地變量表)

2,、    方法區(qū)中靜態(tài)屬性引用的對象

3,、    方法區(qū)中常量引用的對象

4、    本地方法棧中引用的對象(Native對象)

說了這么多,,其實(shí)我們可以看到,,所有的垃圾回收機(jī)制都是和引用相關(guān)的,那我們來具體的來看一下引用的分類,,到底有哪些類型的引用,?每種引用都是做什么的呢,?

Java中存在四種引用,,每種引用如下:

1、  強(qiáng)引用

只要引用存在,,垃圾回收器永遠(yuǎn)不會回收

Object obj = new Object();

//可直接通過obj取得對應(yīng)的對象 obj.equels(new Object());

而這樣 obj對象對后面new Object的一個(gè)強(qiáng)引用,,只有當(dāng)obj這個(gè)引用被釋放之后,對象才會被釋放掉,,這也是我們經(jīng)常所用到的編碼形式,。

2、  軟引用

非必須引用,,內(nèi)存溢出之前進(jìn)行回收,,可以通過以下代碼實(shí)現(xiàn)

Object obj = new Object();

SoftReference<Object> sf = new SoftReference<Object>(obj);

obj = null;

sf.get();//有時(shí)候會返回null

這時(shí)候sf是對obj的一個(gè)軟引用,通過sf.get()方法可以取到這個(gè)對象,,當(dāng)然,,當(dāng)這個(gè)對象被標(biāo)記為需要回收的對象時(shí),則返回null,;
軟引用主要用戶實(shí)現(xiàn)類似緩存的功能,,在內(nèi)存足夠的情況下直接通過軟引用取值,無需從繁忙的真實(shí)來源查詢數(shù)據(jù),,提升速度,;當(dāng)內(nèi)存不足時(shí),自動刪除這部分緩存數(shù)據(jù),,從真正的來源查詢這些數(shù)據(jù),。

3,、  弱引用

第二次垃圾回收時(shí)回收,可以通過如下代碼實(shí)現(xiàn)

Object obj = new Object();

WeakReference<Object> wf = new WeakReference<Object>(obj);

obj = null;

wf.get();//有時(shí)候會返回null

wf.isEnQueued();//返回是否被垃圾回收器標(biāo)記為即將回收的垃圾

弱引用是在第二次垃圾回收時(shí)回收,,短時(shí)間內(nèi)通過弱引用取對應(yīng)的數(shù)據(jù),,可以取到,當(dāng)執(zhí)行過第二次垃圾回收時(shí),,將返回null,。

弱引用主要用于監(jiān)控對象是否已經(jīng)被垃圾回收器標(biāo)記為即將回收的垃圾,可以通過弱引用的isEnQueued方法返回對象是否被垃圾回收器

4,、  虛引用(幽靈/幻影引用)

           垃圾回收時(shí)回收,,無法通過引用取到對象值,可以通過如下代碼實(shí)現(xiàn)

Object obj = new Object();

PhantomReference<Object> pf = new PhantomReference<Object>(obj);

obj=null;

pf.get();//永遠(yuǎn)返回null

pf.isEnQueued();//返回從內(nèi)存中已經(jīng)刪除

虛引用是每次垃圾回收的時(shí)候都會被回收,,通過虛引用的get方法永遠(yuǎn)獲取到的數(shù)據(jù)為null,,因此也被成為幽靈引用。

虛引用主要用于檢測對象是否已經(jīng)從內(nèi)存中刪除,。

在上文中已經(jīng)提到了,,我們的對象在內(nèi)存中會被劃分為5塊區(qū)域,而每塊數(shù)據(jù)的回收比例是不同的,,根據(jù)IBM的統(tǒng)計(jì),,數(shù)據(jù)如下圖所示:

 



 

 我們知道,方法區(qū)主要存放類與類之間關(guān)系的數(shù)據(jù),,而這部分?jǐn)?shù)據(jù)被加載到內(nèi)存之后,,基本上是不會發(fā)生變更的,

Java堆中的數(shù)據(jù)基本上是朝生夕死的,,我們用完之后要馬上回收的,,而Java棧和本地方法棧中的數(shù)據(jù),因?yàn)橛泻筮M(jìn)先出的原則,,當(dāng)我取下面的數(shù)據(jù)之前,,必須要把棧頂?shù)脑爻鰲#虼嘶厥章士烧J(rèn)為是100%,;而程序計(jì)數(shù)器我們前面也已經(jīng)提到,,主要用戶記錄線程執(zhí)行的行號等一些信息,這塊區(qū)域也是被認(rèn)為是唯一一塊不會內(nèi)存溢出的區(qū)域,。在SunHostSpot的虛擬機(jī)中,,對于程序計(jì)數(shù)器是不回收的,而方法區(qū)的數(shù)據(jù)因?yàn)榛厥章史浅P?,而成本又比較高,,一般認(rèn)為是“性價(jià)比”非常差的,所以Sun自己的虛擬機(jī)HotSpot中是不回收的,!但是在現(xiàn)在高性能分布式J2EE的系統(tǒng)中,,我們大量用到了反射,、動態(tài)代理、CGLIB,、JSPOSGI等,,這些類頻繁的調(diào)用自定義類加載器,都需要?jiǎng)討B(tài)的加載和卸載了,,以保證永久帶不會溢出,,他們通過自定義的類加載器進(jìn)行了各項(xiàng)操作,因此在實(shí)際的應(yīng)用開發(fā)中,,類也是被經(jīng)常加載和卸載的,,方法區(qū)也是會被回收的!但是方法區(qū)的回收條件非??量?,只有同時(shí)滿足以下三個(gè)條件才會被回收!

 

1,、所有實(shí)例被回收

2,、加載該類的ClassLoader被回收

3Class對象無法通過任何途徑訪問(包括反射)

好了,,我們現(xiàn)在切入正題,,Java1.2之前主要通過引用計(jì)數(shù)器來標(biāo)記是否需要垃圾回收,而1.2之后都使用根搜索算法來收集垃圾,,而收集后的垃圾是通過什么算法來回收的呢,?

1、    標(biāo)記-清除算法

2,、    復(fù)制算法

3、    標(biāo)記-整理算法

我們來逐一過一下

1,、    標(biāo)記-清除算法

        



 

 

標(biāo)記-清除算法采用從根集合進(jìn)行掃描,,對存活的對象對象標(biāo)記,標(biāo)記完畢后,,再掃描整個(gè)空間中未被標(biāo)記的對象,,進(jìn)行回收,如上圖所示,。

標(biāo)記-清除算法不需要進(jìn)行對象的移動,,并且僅對不存活的對象進(jìn)行處理,在存活對象比較多的情況下極為高效,,但由于標(biāo)記-清除算法直接回收不存活的對象,,因此會造成內(nèi)存碎片!

2,、    復(fù)制算法

 



 
 
 
 

 復(fù)制算法采用從根集合掃描,,并將存活對象復(fù)制到一塊新的,,沒有使用過的空間中,這種算法當(dāng)控件存活的對象比較少時(shí),,極為高效,,但是帶來的成本是需要一塊內(nèi)存交換空間用于進(jìn)行對象的移動。也就是我們前面提到的

s0 s1等空間,。

 

3,、    標(biāo)記-整理算法

 



 
 
 

 標(biāo)記

-整理算法采用標(biāo)記-清除算法一樣的方式進(jìn)行對象的標(biāo)記,但在清除時(shí)不同,,在回收不存活的對象占用的空間后,,會將所有的存活對象往左端空閑空間移動,并更新對應(yīng)的指針,。標(biāo)記-整理算法是在標(biāo)記-清除算法的基礎(chǔ)上,,又進(jìn)行了對象的移動,因此成本更高,,但是卻解決了內(nèi)存碎片的問題,。

 

我們知道,JVM為了優(yōu)化內(nèi)存的回收,,進(jìn)行了分代回收的方式,,對于新生代內(nèi)存的回收(minor GC)主要采用復(fù)制算法,下圖展示了minor GC的執(zhí)行過程,。

 



 
 

 對于新生代和舊生代,,

JVM可使用很多種垃圾回收器進(jìn)行垃圾回收,下圖展示了不同生代不通垃圾回收器,,其中兩個(gè)回收器之間有連線表示這兩個(gè)回收器可以同時(shí)使用,。

 

 



 

 而這些垃圾回收器又分為串行回收方式、并行回收方式合并發(fā)回收方式執(zhí)行,,分別運(yùn)用于不同的場景,。如下圖所示

 

 



 

 下面我們來逐一介紹一下每個(gè)垃圾回收器。

 

1,、    Serial收集器

看名字我們都可以看的出來,,這個(gè)屬于串行收集器。其運(yùn)行示意圖如下



 
 

 Serial

收集器是歷史最悠久的一個(gè)回收器,,JDK1.3之前廣泛使用這個(gè)收集器,目前也是ClientVM ServerVM 44GB以下機(jī)器的默認(rèn)垃圾回收器,。串行收集器并不是只能使用一個(gè)CPU進(jìn)行收集,,而是當(dāng)JVM需要進(jìn)行垃圾回收的時(shí)候,需要中斷所有的用戶線程,,知道它回收結(jié)束為止,,因此又號稱“Stop The World 的垃圾回收器,。注意,,JVM中文名稱為java虛擬機(jī),因此它就像一臺虛擬的電腦一樣在工作,,而其中的每一個(gè)線程就被認(rèn)為是JVM的一個(gè)處理器,因此大家看到圖中的CPU0,、CPU1實(shí)際為用戶的線程,,而不是真正機(jī)器的CPU,大家不要誤解哦,。

 

串行回收方式適合低端機(jī)器,,是Client模式下的默認(rèn)收集器,對CPU和內(nèi)存的消耗不高,,適合用戶交互比較少,,后臺任務(wù)較多的系統(tǒng)。

Serial收集器默認(rèn)新舊生代的回收器搭配為Serial+ SerialOld

2,、    ParNew收集器

ParNew收集器其實(shí)就是多線程版本的Serial收集器,,其運(yùn)行示意圖如下

 



 
 
 

 同樣有

Stop The World的問題,他是多CPU模式下的首選回收器(該回收器在單CPU的環(huán)境下回收效率遠(yuǎn)遠(yuǎn)低于Serial收集器,,所以一定要注意場景哦),,也是Server模式下的默認(rèn)收集器。

 

3,、    ParallelScavenge

ParallelScavenge又被稱為是吞吐量優(yōu)先的收集器,,器運(yùn)行示意圖如下

 



 
 

 ParallelScavenge

所提到的吞吐量=程序運(yùn)行時(shí)間/(JVM執(zhí)行回收的時(shí)間+程序運(yùn)行時(shí)間),假設(shè)程序運(yùn)行了100分鐘,JVM的垃圾回收占用1分鐘,,那么吞吐量就是99%。在當(dāng)今網(wǎng)絡(luò)告訴發(fā)達(dá)的今天,,良好的響應(yīng)速度是提升用戶體驗(yàn)的一個(gè)重要指標(biāo),,多核并行云計(jì)算的發(fā)展要求程序盡可能的使用CPU和內(nèi)存資源,盡快的計(jì)算出最終結(jié)果,,因此在交互不多的云端,,比較適合使用該回收器。

 

4,、    ParallelOld

ParallelOld是老生代并行收集器的一種,,使用標(biāo)記整理算法,、是老生代吞吐量優(yōu)先的一個(gè)收集器。這個(gè)收集器是JDK1.6之后剛引入的一款收集器,,我們看之前那個(gè)圖之間的關(guān)聯(lián)關(guān)系可以看到,,早期沒有ParallelOld之前,吞吐量優(yōu)先的收集器老生代只能使用串行回收收集器,,大大的拖累了吞吐量優(yōu)先的性能,,自從JDK1.6之后,才能真正做到較高效率的吞吐量優(yōu)先,。其運(yùn)行示意圖如下

 



 
  

 5,、

    SerialOld

 

SerialOld是舊生代Client模式下的默認(rèn)收集器,單線程執(zhí)行,;在JDK1.6之前也是ParallelScvenge回收新生代模式下舊生代的默認(rèn)收集器,,同時(shí)也是并發(fā)收集器CMS回收失敗后的備用收集器。其運(yùn)行示意圖如下

 



 

 6,、

    CMS

 

CMS又稱響應(yīng)時(shí)間優(yōu)先(最短回收停頓)的回收器,,使用并發(fā)模式回收垃圾,使用標(biāo)記-清除算法,,CMSCPU是非常敏感的,,它的回收線程數(shù)=CPU+3/4,因此當(dāng)CPU2核的實(shí)惠,,回收線程將占用的CPU資源的50%,而當(dāng)CPU核心數(shù)為4時(shí)僅占用25%,。他的運(yùn)行示意圖如下

 



 

 CMS

模式主要分為4個(gè)過程

 

 


 在初始標(biāo)記的時(shí)候,,需要中斷所有用戶線程,在并發(fā)標(biāo)記階段,,用戶線程和標(biāo)記線程

并發(fā)執(zhí)行,,而在這個(gè)過程中,隨著內(nèi)存引用關(guān)系的變化,,可能會發(fā)生原來標(biāo)記的對象被釋放,,進(jìn)而引發(fā)新的垃圾,因此可能會產(chǎn)生一系列的浮動垃圾,,不能被回收,。

 

CMS 為了確保能夠掃描到所有的對象,避免在Initial Marking 中還有未標(biāo)識到的對象,,采用的方法為找到標(biāo)記了的對象,,并將這些對象放入Stack 中,掃描時(shí)尋找此對象依賴的對象,如果依賴的對象的地址在其之前,,則將此對象進(jìn)行標(biāo)記,,并同時(shí)放入Stack 中,如依賴的對象地址在其之后,,則僅標(biāo)記該對象,。

在進(jìn)行Concurrent Marking 時(shí)minor GC 也可能會同時(shí)進(jìn)行,這個(gè)時(shí)候很容易造成舊生代對象引用關(guān)系改變,,CMS 為了應(yīng)對這樣的并發(fā)現(xiàn)象,,提供了一個(gè)Mod Union Table 來進(jìn)行記錄,在這個(gè)Mod Union Table中記錄每次minor GC 后修改了的Card 的信息,。這也是ParallelScavenge不能和CMS一起使用的原因,。

CMS產(chǎn)生浮動垃圾的情況請見如下示意圖

 



 

 

在運(yùn)行回收過后,c就變成了浮動垃圾,。

由于CMS會產(chǎn)生浮動垃圾,,當(dāng)回收過后,浮動垃圾如果產(chǎn)生過多,,同時(shí)因?yàn)槭褂脴?biāo)記-清除算法會產(chǎn)生碎片,,可能會導(dǎo)致回收過后的連續(xù)空間仍然不能容納新生代移動過來或者新創(chuàng)建的大資源,因此會導(dǎo)致CMS回收失敗,,進(jìn)而觸發(fā)另外一次FULL GC,,而這時(shí)候則采用SerialOld進(jìn)行二次回收。

同時(shí)CMS因?yàn)榭赡墚a(chǎn)生浮動垃圾,,而CMS在執(zhí)行回收的同時(shí)新生代也有可能在進(jìn)行回收操作,,為了保證舊生代能夠存放新生代轉(zhuǎn)移過來的數(shù)據(jù),CMS在舊生代內(nèi)存到達(dá)全部容量的68%就觸發(fā)了CMS的回收,!

7,、    GarbageFirst(G1 )

我們再來看垃圾回收器的總圖,剛才我們可以看到,,我在圖上標(biāo)記了一個(gè),?,其實(shí)這是一個(gè)新的垃圾回收器,,既可以回收新生代也可以回收舊生代,,SunHotSpot 1.6u14以上EarlyAccess版本加入了這個(gè)回收器,sun公司預(yù)期SunHotSpot1.7發(fā)布正式版,,他是商用高性能垃圾回收器,,通過重新劃分內(nèi)存區(qū)域,整合優(yōu)化CMS,,同時(shí)注重吞吐量和響應(yīng)時(shí)間,但是杯具的是被oracle收購之后這個(gè)收集器屬于商用收費(fèi)收集器,,因此目前基本上沒有人使用,,我們在這里也就不多介紹,,更多信息可以參考oracle新版本JDK說明。

下面我們再來看下JVM的一些內(nèi)存分配與回收策略

1,、    優(yōu)先在Edon上分配對象

 

代碼示例

package com.yhj.jvm.gc.edenFirst;

/**

 * @DescribedEdon優(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-下午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-下午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];

    }

 

}

運(yùn)行結(jié)果



 

 結(jié)果分析

 

從運(yùn)行結(jié)果我們可以很清晰的看到,,eden8MB的存儲控件(通過參數(shù)配置),前6MB的數(shù)據(jù)優(yōu)先分配到eden區(qū)域,當(dāng)下一個(gè)2MB存放時(shí),,因空間已滿,,觸發(fā)一次GC,但是這部分?jǐn)?shù)據(jù)因?yàn)闆]有回收(引用還在,,當(dāng)賦值為null后則不會轉(zhuǎn)移),,數(shù)據(jù)會被復(fù)制到s0區(qū)域,但是s0區(qū)域不夠存儲,,因此直接放入老生代區(qū)域,,新的2MB數(shù)據(jù)存放在eden區(qū)域

2、    大對象直接進(jìn)入老生代

 

代碼示例

package com.yhj.jvm.gc.bigObjIntoOld;

/**

 * @Described:大對象直接進(jìn)入老生代測試

 * VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc

 * Edon s0 s1 old

 *   8  1   1  10

 * @author YHJ create at 2012-1-下午05:28:47

 * @FileNmae com.yhj.jvm.gc.bigObjIntoOld.BigObjIntoOld.java

 */

public class BigObjIntoOld {

 

    private final static int ONE_MB = 1024*1024;

 

    /**

     * @param args

     * @Author YHJ create at 2012-1-下午04:44:38

     */

    public static void main(String[] args) {

       @SuppressWarnings("unused")

       byte[] testCase1,testCase2,testCase3,testCase4;

       testCase1 = new byte[8*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];

    }

 

}

運(yùn)行結(jié)果



 

 結(jié)果分析

 

我們看到,,沒有觸發(fā)GC日志,,而數(shù)據(jù)是直接進(jìn)入老生代的

3、    年長者(長期存活對象)進(jìn)入老生代

 

代碼示例:

package com.yhj.jvm.gc.longLifeTimeIntoOld;

/**

 * @Described:當(dāng)年齡大于一定值的時(shí)候進(jìn)入老生代  默認(rèn)值15

 * VM params : -Xms20M -Xmx20M -Xmn10M -XX:MaxTenuringThreshold=1-XX:+PrintGCDetails -verbose:gc

 * Edon s0 s1 old  age

 *   8  1   1  10   1

 * @author YHJ create at 2012-1-下午05:39:16

 * @FileNmaecom.yhj.jvm.gc.longLifeTimeIntoOld.LongLifeTimeIntoOld.java

 */

public class LongLifeTimeIntoOld {

 

    private final static int ONE_MB = 1024*1024;

 

    /**

     * @param args

     * @Author YHJ create at 2012-1-下午04:44:38

     */

    public static void main(String[] args) {

       @SuppressWarnings("unused")

       byte[] testCase1,testCase2,testCase3,testCase4;

       testCase1 = new byte[1*ONE_MB/4];

       testCase2 = new byte[7*ONE_MB+3*ONE_MB/4];

       testCase2 = null;

       testCase3 = new byte[7*ONE_MB+3*ONE_MB/4];

       testCase3 = null;

       testCase4 = new byte[ONE_MB];

    }

 

}

 

運(yùn)行結(jié)果



 

 結(jié)果分析

 

從代碼中我們可以看到,,當(dāng)testCase1劃分為0.25MB數(shù)據(jù),,進(jìn)行多次大對象創(chuàng)建之后,testCase1應(yīng)該在GC執(zhí)行之后被復(fù)制到s0區(qū)域(s0足以容納testCase1),,但是我們設(shè)置了對象的年齡為1,,即超過1歲便進(jìn)入老生代,因此GC執(zhí)行2次后testCase1直接被復(fù)制到了老生代,,而默認(rèn)進(jìn)入老生代的年齡為15,。我們通過profilter的監(jiān)控工具可以很清楚的看到對象的年齡,如圖所示

 



 

 右側(cè)的年代數(shù)目就是對象的年齡

 

4,、    群體效應(yīng)(大批中年對象進(jìn)入老生代)

 

代碼示例

package com.yhj.jvm.gc.dynamicMoreAVG_intoOld;

/**

 * @Describeds0占用空間到達(dá)50%直接進(jìn)入老生代

 * 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-下午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-下午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];

      

    }

 

}

運(yùn)行結(jié)果



 

 結(jié)果分析

 

我們看到,,當(dāng)創(chuàng)建后testCase3testCase2被移動到s0區(qū)域,,當(dāng)被釋放后,,繼續(xù)創(chuàng)建testCase3,按理說testCase2應(yīng)該移動到s1區(qū)域,,但是因?yàn)槌^了s1區(qū)域的1/2,,因此直接進(jìn)入老生代

5、    擔(dān)保GC(擔(dān)保minorGC)

擔(dān)保GC就是擔(dān)保minorGC能夠滿足當(dāng)前的存儲空間,,而無需觸發(fā)老生代的回收,,由于大部分對象都是朝生夕死的,因此,在實(shí)際開發(fā)中這種很起效,,但是也有可能會發(fā)生擔(dān)保失敗的情況,,當(dāng)擔(dān)保失敗的時(shí)候會觸發(fā)FullGC,但是失敗畢竟是少數(shù),,因此這種一般是很劃算的,。

 

代碼示例

package com.yhj.jvm.gc.securedTransactions;

/**

 * @Described:擔(dān)保交易測試

 * VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc-XX:-HandlePromotionFailure  無擔(dān)保

 * VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc-XX:+HandlePromotionFailure  有擔(dān)保

 * Edon s0 s1 old  

 *   8  1   1  10  

 * @author YHJ create at 2012-1-下午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-下午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];

      

    }

 

}

 運(yùn)行結(jié)果

1、  無擔(dān)保


 2,、



   有擔(dān)保

 



 

 結(jié)果分析

 

 

我們可以很清楚的看到,,當(dāng)無擔(dān)保的時(shí)候,觸發(fā)了一次FullGC 而有擔(dān)保的情況下,,只有monorGC則完成了回收,,大大提升了效率。

當(dāng)我們注釋掉對應(yīng)的代碼

 

//     testCase4 = null;

//     testCase5 = null;

//     testCase6 = null;

 

的時(shí)候,,就會引發(fā)擔(dān)保失敗,,如下圖所示

 


 JVM

默認(rèn)情況是是開啟擔(dān)保的,無需設(shè)置參數(shù),。

 

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多