本文主要是基于Sun JDK 1.6 Garbage Collector(作者:畢玄)的整理與總結(jié),原文請(qǐng)讀者在網(wǎng)上搜索。

1、Java虛擬機(jī)運(yùn)行時(shí)的數(shù)據(jù)區(qū)

image

2、常用的內(nèi)存區(qū)域調(diào)節(jié)參數(shù)

-Xms:初始堆大小,默認(rèn)為物理內(nèi)存的1/64(<1GB);默認(rèn)(MinHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存小于40%時(shí),JVM就會(huì)增大堆直到-Xmx的最大限制

-Xmx:最大堆大小,默認(rèn)(MaxHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存大于70%時(shí),JVM會(huì)減少堆直到 -Xms的最小限制

-Xmn:新生代的內(nèi)存空間大小,注意:此處的大小是(eden+ 2 survivor space)。與jmap -heap中顯示的New gen是不同的。整個(gè)堆大小=新生代大小 + 老生代大小 + 永久代大小。
在保證堆大小不變的情況下,增大新生代后,將會(huì)減小老生代大小。此值對(duì)系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8。

-XX:SurvivorRatio:新生代中Eden區(qū)域與Survivor區(qū)域的容量比值,默認(rèn)值為8。兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:8,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/10。

-Xss:每個(gè)線程的堆棧大小。JDK5.0以后每個(gè)線程堆棧大小為1M,以前每個(gè)線程堆棧大小為256K。應(yīng)根據(jù)應(yīng)用的線程所需內(nèi)存大小進(jìn)行適當(dāng)調(diào)整。在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程。但是操作系統(tǒng)對(duì)一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無限生成,經(jīng)驗(yàn)值在3000~5000左右。一般小的應(yīng)用, 如果棧不是很深, 應(yīng)該是128k夠用的,大的應(yīng)用建議使用256k。這個(gè)選項(xiàng)對(duì)性能影響比較大,需要嚴(yán)格的測(cè)試。和threadstacksize選項(xiàng)解釋很類似,官方文檔似乎沒有解釋,在論壇中有這樣一句話:"-Xss is translated in a VM flag named ThreadStackSize”一般設(shè)置這個(gè)值就可以了。

-XX:PermSize:設(shè)置永久代(perm gen)初始值。默認(rèn)值為物理內(nèi)存的1/64。

-XX:MaxPermSize:設(shè)置持久代最大值。物理內(nèi)存的1/4。

3、內(nèi)存分配方法

1)堆上分配   2)棧上分配  3)堆外分配(DirectByteBuffer或直接使用Unsafe.allocateMemory,但不推薦這種方式)

4、監(jiān)控方法

1)系統(tǒng)程序運(yùn)行時(shí)可通過jstat –gcutil來查看堆中各個(gè)內(nèi)存區(qū)域的變化以及GC的工作狀態(tài);
2)啟動(dòng)時(shí)可添加-XX:+PrintGCDetails  –Xloggc:<file>輸出到日志文件來查看GC的狀況;
3)jmap –heap可用于查看各個(gè)內(nèi)存空間的大??;

5)斷代法可用GC匯總

image

一、新生代可用GC

1)串行GC(Serial Copying):client模式下默認(rèn)GC方式,也可通過-XX:+UseSerialGC來強(qiáng)制指定;默認(rèn)情況下 eden、s0、s1的大小通過-XX:SurvivorRatio來控制,默認(rèn)為8,含義
為eden:s0的比例,啟動(dòng)后可通過jmap –heap [pid]來查看。

      默認(rèn)情況下,僅在TLAB或eden上分配,只有兩種情況下會(huì)在老生代分配:
      1、需要分配的內(nèi)存大小超過eden space大小;
      2、在配置了PretenureSizeThreshold的情況下,對(duì)象大小大于此值。

      默認(rèn)情況下,觸發(fā)Minor GC時(shí):
      之前Minor GC晉級(jí)到old的平均大小 < 老生代的剩余空間 < eden+from Survivor的使用空間。當(dāng)HandlePromotionFailure為true,則僅觸發(fā)minor gc;如為false,則觸發(fā)full GC。

      默認(rèn)情況下,新生代對(duì)象晉升到老生代的規(guī)則:

     1、經(jīng)歷多次minor gc仍存活的對(duì)象,可通過以下參數(shù)來控制:以MaxTenuringThreshold值為準(zhǔn),默認(rèn)為15。
     2、to space放不下的,直接放入老生代;

2)并行GC(ParNew):CMS GC時(shí)默認(rèn)采用,也可采用-XX:+UseParNewGC強(qiáng)制指定;垃圾回收的時(shí)候采用多線程的方式。

3)并行回收GC(Parallel Scavenge):server模式下默認(rèn)的GC方式,也可采用-XX:+UseParallelGC強(qiáng)制指定;eden、s0、s1的大小可通過-XX:SurvivorRatio來控制,但默認(rèn)情況下
以-XX:InitialSurivivorRatio為準(zhǔn),此值默認(rèn)為8,代表的為新生代大小 : s0,這點(diǎn)要特別注意。

      默認(rèn)情況下,當(dāng)TLAB、eden上分配都失敗時(shí),判斷需要分配的內(nèi)存大小是否 >= eden space的一半大小,如是就直接在老生代上分配;

      默認(rèn)情況下的垃圾回收規(guī)則:

      1、在回收前PS GC會(huì)先檢測(cè)之前每次PS GC時(shí),晉升到老生代的平均大小是否大于老生代的剩余空間,如大于則直接觸發(fā)full GC;
      2、在回收后,也會(huì)按照上面的規(guī)則進(jìn)行檢測(cè)。

      默認(rèn)情況下的新生代對(duì)象晉升到老生代的規(guī)則:
     1、經(jīng)歷多次minor gc仍存活的對(duì)象,可通過以下參數(shù)來控制:AlwaysTenure,默認(rèn)false,表示只要minor GC時(shí)存活,就晉升到老生代;NeverTenure,默認(rèn)false,表示永不晉升到老生代;上面兩個(gè)都沒設(shè)置的情冴下,如UseAdaptiveSizePolicy,啟動(dòng)時(shí)以InitialTenuringThreshold值作為存活次數(shù)的閾值,在每次ps gc后會(huì)動(dòng)態(tài)調(diào)整,如不使用UseAdaptiveSizePolicy,則以MaxTenuringThreshold為準(zhǔn)。
     2、to space放不下的,直接放入老生代。

     在回收后,如UseAdaptiveSizePolicy,PS GC會(huì)根據(jù)運(yùn)行狀態(tài)動(dòng)態(tài)調(diào)整eden、to以及TenuringThreshold的大小。如果不希望動(dòng)態(tài)調(diào)整可設(shè)置-XX:-UseAdaptiveSizePolicy。如希望跟蹤每次的變化情況,可在啟勱參數(shù)上增加: PrintAdaptiveSizePolicy。

二、老生代可用GC

1、串行GC(Serial Copying):client方式下默認(rèn)GC方式,可通過-XX:+UseSerialGC強(qiáng)制指定。

    觸發(fā)機(jī)制匯總:
   1)old gen空間不足;
   2)perm gen空間不足;
   3)minor gc時(shí)的悲觀策略;
   4)minor GC后在eden上分配內(nèi)存仍然失?。?br>   5)執(zhí)行heap dump時(shí);
   6)外部調(diào)用System.gc,可通過-XX:+DisableExplicitGC來禁止。

2、并行回收GC(Parallel Scavenge): server模式下默認(rèn)GC方式,可通過-XX:+UseParallelGC強(qiáng)制指定; 并行的線程數(shù)為當(dāng)cpu core<=8 ? cpu core : 3+(cpu core*5)/8或通過-XX:ParallelGCThreads=x來強(qiáng)制指定。如ScavengeBeforeFullGC為true(默認(rèn)值),則先執(zhí)行minor GC。

3、并行Compacting:可通過-XX:+UseParallelOldGC強(qiáng)制指定。

4、并發(fā)CMS:可通過-XX:+UseConcMarkSweepGC來強(qiáng)制指定。并發(fā)的線程數(shù)默認(rèn)為:( 并行GC線程數(shù)+3)/4,也可通過ParallelCMSThreads指定。

    觸發(fā)機(jī)制:
    1、當(dāng)老生代空間的使用到達(dá)一定比率時(shí)觸發(fā);

     Hotspot V 1.6中默認(rèn)為65%,可通過PrintCMSInitiationStatistics(此參數(shù)在V 1.5中不能用)來查看這個(gè)值到底是多少;可通過CMSInitiatingOccupancyFraction來強(qiáng)制指定,默認(rèn)值并不是賦值在了這個(gè)值上,是根據(jù)如下公式計(jì)算出來的: ((100 - MinHeapFreeRatio) +(double)(CMSTriggerRatio * MinHeapFreeRatio) / 100.0)/ 100.0; 其中,MinHeapFreeRatio默認(rèn)值: 40   CMSTriggerRatio默認(rèn)值: 80。

     2、當(dāng)perm gen采用CMS收集且空間使用到一定比率時(shí)觸發(fā);

     perm gen采用CMS收集需設(shè)置:-XX:+CMSClassUnloadingEnabled   Hotspot V 1.6中默認(rèn)為65%;可通過CMSInitiatingPermOccupancyFraction來強(qiáng)制指定,同樣,它是根據(jù)如下公式計(jì)算出來的:((100 - MinHeapFreeRatio) +(double)(CMSTriggerPermRatio* MinHeapFreeRatio) / 100.0)/ 100.0; 其中,MinHeapFreeRatio默認(rèn)值: 40    CMSTriggerPermRatio默認(rèn)值: 80。

      3、Hotspot根據(jù)成本計(jì)算決定是否需要執(zhí)行CMS GC;可通過-XX:+UseCMSInitiatingOccupancyOnly來去掉這個(gè)動(dòng)態(tài)執(zhí)行的策略。
      4、外部調(diào)用了System.gc,且設(shè)置了ExplicitGCInvokesConcurrent;需要注意,在hotspot 6中,在這種情況下如應(yīng)用同時(shí)使用了NIO,可能會(huì)出現(xiàn)bug。

6、GC組合

1)默認(rèn)GC組合

image

2)可選的GC組合

image

7、GC監(jiān)測(cè)

1)jstat –gcutil [pid] [intervel] [count]
2)-verbose:gc // 可以輔助輸出一些詳細(xì)的GC信息;-XX:+PrintGCDetails // 輸出GC詳細(xì)信息;-XX:+PrintGCApplicationStoppedTime // 輸出GC造成應(yīng)用暫停的時(shí)間
-XX:+PrintGCDateStamps // GC發(fā)生的時(shí)間信息;-XX:+PrintHeapAtGC // 在GC前后輸出堆中各個(gè)區(qū)域的大小;-Xloggc:[file] // 將GC信息輸出到單獨(dú)的文件中,建議都加上,這個(gè)消耗不大,而且對(duì)查問題和調(diào)優(yōu)有很大的幫助。gc的日志拿下來后可使用GCLogViewer或gchisto進(jìn)行分析。
3)圖形化的情況下可直接用jvisualvm進(jìn)行分析。

4)查看內(nèi)存的消耗狀況

      (1)長(zhǎng)期消耗,可以直接dump,然后MAT(內(nèi)存分析工具)查看即可

      (2)短期消耗,圖形界面情況下,可使用jvisualvm的memory profiler或jprofiler。

8、系統(tǒng)調(diào)優(yōu)方法

步驟:1、評(píng)估現(xiàn)狀 2、設(shè)定目標(biāo) 3、嘗試調(diào)優(yōu) 4、衡量調(diào)優(yōu) 5、細(xì)微調(diào)整

設(shè)定目標(biāo):

1)降低Full GC的執(zhí)行頻率?
2)降低Full GC的消耗時(shí)間?
3)降低Full GC所造成的應(yīng)用停頓時(shí)間?
4)降低Minor GC執(zhí)行頻率?
5)降低Minor GC消耗時(shí)間?
例如某系統(tǒng)的GC調(diào)優(yōu)目標(biāo):降低Full GC執(zhí)行頻率的同時(shí),盡可能降低minor GC的執(zhí)行頻率、消耗時(shí)間以及GC對(duì)應(yīng)用造成的停頓時(shí)間。

衡量調(diào)優(yōu):

1、衡量工具
1)打印GC日志信息:-XX:+PrintGCDetails –XX:+PrintGCApplicationStoppedTime -Xloggc: {文件名}  -XX:+PrintGCTimeStamps
2)jmap:(由于每個(gè)版本jvm的默認(rèn)值可能會(huì)有改變,建議還是用jmap首先觀察下目前每個(gè)代的內(nèi)存大小、GC方式) ?
3)運(yùn)行狀況監(jiān)測(cè)工具:jstat、jvisualvm、sar 、gclogviewer

2、應(yīng)收集的信息
1)minor gc的執(zhí)行頻率;full gc的執(zhí)行頻率,每次GC耗時(shí)多少?
2)高峰期什么狀況?
3)minor gc回收的效果如何?survivor的消耗狀況如何,每次有多少對(duì)象會(huì)進(jìn)入老生代?
4)full gc回收的效果如何?(簡(jiǎn)單的memory leak判斷方法)
5)系統(tǒng)的load、cpu消耗、qps or tps、響應(yīng)時(shí)間

QPS每秒查詢率:是對(duì)一個(gè)特定的查詢服務(wù)器在規(guī)定時(shí)間內(nèi)所處理流量多少的衡量標(biāo)準(zhǔn)。在因特網(wǎng)上,作為域名服務(wù)器的機(jī)器性能經(jīng)常用每秒查詢率來衡量。對(duì)應(yīng)fetches/sec,即每秒的響應(yīng)請(qǐng)求數(shù),也即是最大吞吐能力。
TPS(Transaction Per Second):每秒鐘系統(tǒng)能夠處理的交易或事務(wù)的數(shù)量。

嘗試調(diào)優(yōu):

注意Java RMI的定時(shí)GC觸發(fā)機(jī)制,可通過:-XX:+DisableExplicitGC來禁止或通過 -Dsun.rmi.dgc.server.gcInterval=3600000來控制觸發(fā)的時(shí)間。

1)降低Full GC執(zhí)行頻率 – 通常瓶頸
老生代本身占用的內(nèi)存空間就一直偏高,所以只要稍微放點(diǎn)對(duì)象到老生代,就full GC了;
通常原因:系統(tǒng)緩存的東西太多;
例如:使用oracle 10g驅(qū)動(dòng)時(shí)preparedstatement cache太大;
查找辦法:現(xiàn)執(zhí)行Dump然后再進(jìn)行MAT分析;

(1)Minor GC后總是有對(duì)象不斷的進(jìn)入老生代,導(dǎo)致老生代不斷的滿
通常原因:Survivor太小了
系統(tǒng)表現(xiàn):系統(tǒng)響應(yīng)太慢、請(qǐng)求量太大、每次請(qǐng)求分配的內(nèi)存太多、分配的對(duì)象太大...
查找辦法:分析兩次minor GC之間到底哪些地方分配了內(nèi)存;
利用jstat觀察Survivor的消耗狀況,-XX:PrintHeapAtGC,輸出GC前后的詳細(xì)信息;
對(duì)于系統(tǒng)響應(yīng)慢可以采用系統(tǒng)優(yōu)化,不是GC優(yōu)化的內(nèi)容;

(2)老生代的內(nèi)存占用一直偏高
調(diào)優(yōu)方法:① 擴(kuò)大老生代的大?。p少新生代的大小或調(diào)大heap的 大?。?;
減少new注意對(duì)minor gc的影響并且同時(shí)有可能造成full gc還是嚴(yán)重;
調(diào)大heap注意full gc的時(shí)間的延長(zhǎng),cpu夠強(qiáng)悍嘛,os是32 bit的嗎?
② 程序優(yōu)化(去掉一些不必要的緩存)

(3)Minor GC后總是有對(duì)象不斷的進(jìn)入老生代
前提:這些進(jìn)入老生代的對(duì)象在full GC時(shí)大部分都會(huì)被回收
調(diào)優(yōu)方法:
① 降低Minor GC的執(zhí)行頻率;
② 讓對(duì)象盡量在Minor GC中就被回收掉:增大Eden區(qū)、增大survivor、增大TenuringThreshold;注意這些可能會(huì)造成minor gc執(zhí)行頻繁;
③ 切換成CMS GC:老生代還沒有滿就回收掉,從而降低Full GC觸發(fā)的可能性;
④ 程序優(yōu)化:提升響應(yīng)速度、降低每次請(qǐng)求分配的內(nèi)存、

(4)降低單次Full GC的執(zhí)行時(shí)間
通常原因:老生代太大了...
調(diào)優(yōu)方法:1)是并行GC嗎?   2)升級(jí)CPU  3)減小Heap或老生代

(5)降低Minor GC執(zhí)行頻率
通常原因:每次請(qǐng)求分配的內(nèi)存多、請(qǐng)求量大
通常辦法:1)擴(kuò)大heap、擴(kuò)大新生代、擴(kuò)大eden。注意點(diǎn):降低每次請(qǐng)求分配的內(nèi)存;橫向增加機(jī)器的數(shù)量分擔(dān)請(qǐng)求的數(shù)量。

(6)降低Minor GC執(zhí)行時(shí)間
通常原因:新生代太大了,響應(yīng)速度太慢了,導(dǎo)致每次Minor GC時(shí)存活的對(duì)象多
通常辦法:1)減小點(diǎn)新生代吧;2)增加CPU的數(shù)量、升級(jí)CPU的配置;加快系統(tǒng)的響應(yīng)速度

細(xì)微調(diào)整:

首先需要了解以下情況:

① 當(dāng)響應(yīng)速度下降到多少或請(qǐng)求量上漲到多少時(shí),系統(tǒng)會(huì)宕掉?

② 參數(shù)調(diào)整后系統(tǒng)多久會(huì)執(zhí)行一次Minor GC,多久會(huì)執(zhí)行一次Full GC,高峰期會(huì)如何?

需要計(jì)算的量:

①每次請(qǐng)求平均需要分配多少內(nèi)存?系統(tǒng)的平均響應(yīng)時(shí)間是多少呢?請(qǐng)求量是多少、多常時(shí)間執(zhí)行一次Minor GC、Full GC?

②現(xiàn)有參數(shù)下,應(yīng)該是多久一次Minor GC、Full GC,對(duì)比真實(shí)狀況,做一定的調(diào)整;

必殺技:提升響應(yīng)速度、降低每次請(qǐng)求分配的內(nèi)存?

9、系統(tǒng)調(diào)優(yōu)舉例

     現(xiàn)象:1、系統(tǒng)響應(yīng)速度大概為100ms;2、當(dāng)系統(tǒng)QPS增長(zhǎng)到40時(shí),機(jī)器每隔5秒就執(zhí)行一次minor gc,每隔3分鐘就執(zhí)行一次full gc,并且很快就一直full GC了;4、每次Full gc后舊生代大概會(huì)消耗400M,有點(diǎn)多了。

     解決方案:解決Full GC次數(shù)過多的問題

    (1)降低響應(yīng)時(shí)間或請(qǐng)求次數(shù),這個(gè)需要重構(gòu),比較麻煩;——這個(gè)是終極方法,往往能夠順利的解決問題,因?yàn)榇蟛糠值膯栴}均是由程序自身造成的。

    (2)減少老生代內(nèi)存的消耗,比較靠譜;——可以通過分析Dump文件(jmap dump),并利用MAT查找內(nèi)存消耗的原因,從而發(fā)現(xiàn)程序中造成老生代內(nèi)存消耗的原因。

    (3)減少每次請(qǐng)求的內(nèi)存的消耗,貌似比較靠譜;——這個(gè)是海市蜃樓,沒有太好的辦法。

    (4)降低GC造成的應(yīng)用暫停的時(shí)間——可以采用CMS GS垃圾回收器。參數(shù)設(shè)置如下:

     -Xms1536m -Xmx1536m -Xmn700m -XX:SurvivorRatio=7 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection

     -XX:CMSMaxAbortablePrecleanTime=1000 -XX:+CMSClassUnloadingEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:+DisableExplicitGC

    (5)減少每次minor gc晉升到old的對(duì)象??蛇x方法:1) 調(diào)大新生代。2)調(diào)大Survivor。3)調(diào)大TenuringThreshold。

      調(diào)大Survivor:當(dāng)前采用PS GC,Survivor space會(huì)被動(dòng)態(tài)調(diào)整。由于調(diào)整幅度很小,導(dǎo)致了經(jīng)常有對(duì)象直接轉(zhuǎn)移到了老生代;于是禁止Survivor區(qū)的動(dòng)態(tài)調(diào)整了,-XX:-UseAdaptiveSizePolicy,并計(jì)算Survivor Space需要的大小,于是繼續(xù)觀察,并做微調(diào)…。最終將Full GC推遲到2小時(shí)1次。

10、垃圾回收的實(shí)現(xiàn)原理

      內(nèi)存回收的實(shí)現(xiàn)方法:1)引用計(jì)數(shù):不適合復(fù)雜對(duì)象的引用關(guān)系,尤其是循環(huán)依賴的場(chǎng)景。2)有向圖Tracing:適合于復(fù)雜對(duì)象的引用關(guān)系場(chǎng)景,Hotspot采用這種。常用算法:Copying、Mark-Sweep、Mark-Compact。

      Hotspot從root set開始掃描有引用的對(duì)象并對(duì)Reference類型的對(duì)象進(jìn)行特殊處理。
      以下是Root Set的列表:1)當(dāng)前正在執(zhí)行的線程;2)全局/靜態(tài)變量;3)JVM Handles;4)JNI 【 Java Native Interface 】Handles;

      另外:minor GC只掃描新生代,當(dāng)老生代的對(duì)象引用了新生代的對(duì)象時(shí),會(huì)采用如下的處理方式:在給對(duì)象賦引用時(shí),會(huì)經(jīng)過一個(gè)write barrier的過程,以便檢查是否有老生代引用新生代對(duì)象的情況,如有則記錄到remember set中。并在minor gc時(shí),remember set指向的新生代對(duì)象也作為root set。

     新生代串行GC(Serial Copying):

     新生代串行GC(Serial Copying)完整內(nèi)存的分配策略:

     1)首先在TLAB(本地線程分配緩沖區(qū))上嘗試分配;
     2)檢查是否需要在新生代上分配,如需要分配的大小小于PretenureSizeThreshold,則在eden區(qū)上進(jìn)行分配,分配成功則返回;分配失敗則繼續(xù);
     3)檢查是否需要嘗試在老生代上分配,如需要,則遍歷所有代并檢查是否可在該代上分配,如可以則進(jìn)行分配;如不需要在老生代上嘗試分配,則繼續(xù);
     4)根據(jù)策略決定執(zhí)行新生代GC或Full GC,執(zhí)行full gc時(shí)不清除soft Ref;
     5)如需要分配的大小大于PretenureSizeThreshold,嘗試在老生代上分配,否則嘗試在新生代上分配;
     6)嘗試擴(kuò)大堆并分配;
     7)執(zhí)行full gc,并清除所有soft Ref,按步驟5繼續(xù)嘗試分配。  

     新生代串行GC(Serial Copying)完整內(nèi)存回收策略
     1)檢查to是否為空,不為空返回false;
     2)檢查老生代剩余空間是否大于當(dāng)前eden+from已用的大小,如大于則返回true,如小于且HandlePromotionFailure為true,則檢查剩余空間是否大于之前每次minor gc晉級(jí)到老生代的平均大小,如大于返回true,如小于返回false。
     3)如上面的結(jié)果為false,則執(zhí)行full gc;如上面的結(jié)果為true,執(zhí)行下面的步驟;
     4)掃描引用關(guān)系,將活的對(duì)象copy到to space,如對(duì)象在minor gc中的存活次數(shù)超過tenuring_threshold或分配失敗,則往老生代復(fù)制,如仍然復(fù)制失敗,則取決于HandlePromotionFailure,如不需要處理,直接拋出OOM,并退出vm,如需處理,則保持這些新生代對(duì)象不動(dòng);

    新生代可用GC-PS

    完整內(nèi)存分配策略
    1)先在TLAB上分配,分配失敗則直接在eden上分配;
    2)當(dāng)eden上分配失敗時(shí),檢查需要分配的大小是否 >= eden space的一半,如是,則直接在老生代分配;
    3)如分配仍然失敗,且gc已超過頻率,則拋出OOM;
    4)進(jìn)入基本分配策略失敗的模式;
    5)執(zhí)行PS GC,在eden上分配;
    6)執(zhí)行非最大壓縮的full gc,在eden上分配;
    7)在舊生代上分配;
    8)執(zhí)行最大壓縮full gc,在eden上分配;
    9)在舊生代上分配;
    10)如還失敗,回到2。

   最悲慘的情況,分配觸發(fā)多次PS GC和多次Full GC,直到OOM。

   完整內(nèi)存回收策略
   1)如gc所執(zhí)行的時(shí)間超過,直接結(jié)束;
   2)先調(diào)用invoke_nopolicy
       2.1 先檢查是不是要嘗試scavenge;
       2.1.1 to space必須為空,如不為空,則返回false;
       2.1.2 獲取之前所有minor gc晉級(jí)到old的平均大小,并對(duì)比目前eden+from已使用的大小,取更小的一個(gè)值,如老生代剩余空間小于此值,則返回false,如大于則返回true;
       2.2 如不需要嘗試scavenge,則返回false,否則繼續(xù);
       2.3 多線程掃描活的對(duì)象,并基亍copying算法回收,回收時(shí)相應(yīng)的晉升對(duì)象到舊生代;
       2.4 如UseAdaptiveSizePolicy,那么重新計(jì)算to space和tenuringThreshold的值,并調(diào)整。
   3)如invoke_nopolicy返回的是false,或之前所有minor gc晉級(jí)到老生代的平均大小 > 舊生代的剩余空間,那么繼續(xù)下面的步驟,否則結(jié)束;
   4)如UseParallelOldGC,則執(zhí)行PSParallelCompact,如不是UseParallelOldGC,則執(zhí)行PSMarkSweep。

    老生代并行CMS GC:

    優(yōu)缺點(diǎn):

    1) 大部分時(shí)候和應(yīng)用并發(fā)進(jìn)行,因此只會(huì)造成很短的暫停時(shí)間;
    2)浮動(dòng)垃圾,沒辦法,所以內(nèi)存空間要稍微大一點(diǎn);
    3)內(nèi)存碎片,-XX:+UseCMSCompactAtFullCollection 來解決;
    4) 爭(zhēng)搶CPU,這GC方式就這樣;
    5)多次remark,所以總的gc時(shí)間會(huì)比并行的長(zhǎng);
    6)內(nèi)存分配,free list方式,so性能稍差,對(duì)minor GC會(huì)有一點(diǎn)影響;
    7)和應(yīng)用并發(fā),有可能分配和回收同時(shí),產(chǎn)生競(jìng)爭(zhēng),引入了鎖,JVM分配優(yōu)先。

11、TLAB的解釋

     堆內(nèi)的對(duì)象數(shù)據(jù)是各個(gè)線程所共享的,所以當(dāng)在堆內(nèi)創(chuàng)建新的對(duì)象時(shí),就需要進(jìn)行鎖操作。鎖操作是比較耗時(shí),因此JVM為每個(gè)線在堆上分配了一塊“自留地”——TLAB(全稱是Thread Local Allocation Buffer),位于堆內(nèi)存的新生代,也就是Eden區(qū)。每個(gè)線程在創(chuàng)建新的對(duì)象時(shí),會(huì)首先嘗試在自己的TLAB里進(jìn)行分配,如果成功就返回,失敗了再到共享的Eden區(qū)里去申請(qǐng)空間。在線程自己的TLAB區(qū)域創(chuàng)建對(duì)象失敗一般有兩個(gè)原因:一是對(duì)象太大,二是自己的TLAB區(qū)剩余空間不夠。通常默認(rèn)的TLAB區(qū)域大小是Eden區(qū)域的1%,當(dāng)然也可以手工進(jìn)行調(diào)整,對(duì)應(yīng)的JVM參數(shù)是-XX:TLABWasteTargetPercent。

參考文獻(xiàn):

1、Sun JDK 1.6 GC(Garbage Collector)  作者:畢玄