JVM堆(Heap)= 新生代(Young) + 舊生代(Tenured)
新生代(Young)= Eden區(qū) + Survivor區(qū)
http://blog.csdn.net/jollyant/article/details/5647141
http://blog.csdn.net/zhangren07/article/details/6270895
http://blog.csdn.net/cutesource/article/details/5906705
http://www.linuxidc.com/Linux/2011-05/36506.htm
JVM學(xué)習(xí)
JVM學(xué)習(xí)2
JVM垃圾回收與性能調(diào)優(yōu)總結(jié)
JVM問(wèn)題
堆/Heap
JVM管理的內(nèi)存叫堆:
在32Bit操作系統(tǒng)上有4G的限制,一般來(lái)說(shuō)Windows下為2G杂瘸,而Linux下為3G叉讥;64Bit的就沒(méi)有這個(gè)限制嚣州。
TLAB:
JVM所占用的主要內(nèi)存都是從堆空間分配的尤筐,堆是所有線程共享的您朽,因此在堆上分配內(nèi)存需要加鎖黑界,Sun JDK為提升效率劳澄,會(huì)為每個(gè)新建的線程在Eden上分配一塊獨(dú)立的空間由該線程獨(dú)享稍走,這塊空間稱為T(mén)LAB(Thread Local Allocation Buffer)袁翁。其大小由JVM根據(jù)運(yùn)行情況計(jì)算得到,也可通過(guò)參數(shù)-XX:TLABWasteTargetPercent來(lái)設(shè)置TLAB可占用的Eden空間的百分比婿脸,默認(rèn)值為1%粱胜。在TLAB上分配內(nèi)存不需要加鎖,因此JVM在給線程中的對(duì)象分配內(nèi)存時(shí)會(huì)盡量在TLAB上分配盖淡。如果對(duì)象過(guò)大或TLAB用完年柠,則仍然在堆上進(jìn)行分配。
JVM初始分配的內(nèi)存由-Xms指定褪迟,默認(rèn)是物理內(nèi)存的1/64但小于1G冗恨。
JVM最大分配的內(nèi)存由-Xmx指定,默認(rèn)是物理內(nèi)存的 1/4但小于1G味赃。
默認(rèn)空余堆內(nèi)存小于40%時(shí)掀抹,JVM就會(huì)增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指 定心俗。
默認(rèn)空余堆內(nèi)存大于70%時(shí)傲武,JVM會(huì)減少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=指定城榛。
服務(wù)器一般設(shè)置-Xms揪利、-Xmx相等以避免在每次GC后調(diào)整堆的大小,所以上面的兩個(gè)參數(shù)沒(méi)啥用狠持。
分代/堆模型
分代是Java垃圾收集的一大亮點(diǎn)疟位,根據(jù)對(duì)象的生命周期長(zhǎng)短,把堆分為3個(gè)代:Young喘垂,Old和Permanent甜刻,根據(jù)不同代的特點(diǎn)采用不同 的收集算法,可以揚(yáng)長(zhǎng)避短正勒。
分區(qū)作用:
新創(chuàng)建的對(duì)象通常先將其分配在新生代中得院,在新生代中經(jīng)過(guò)若干次GC之后仍未釋放的對(duì)象,再將它移動(dòng)到舊生代章贞。為了讓內(nèi)存回收更高效(GC會(huì)暫停JVM中的應(yīng)用)祥绞,Sun JDK在1.2開(kāi)始對(duì)堆采用了分代管理的方式。在分配對(duì)象遇到內(nèi)存不足時(shí),先對(duì)新生代進(jìn)行GC(Young GC)蜕径;當(dāng)新生代GC之后仍無(wú)法滿足內(nèi)存空間分配需求時(shí)怪蔑, 才會(huì)對(duì)整個(gè)堆空間以及方法區(qū)進(jìn)行GC (Full GC)
Young(Nursery):年輕代
研究表明大部分對(duì)象都是朝生暮死,隨生隨滅的丧荐。所以對(duì)于年輕代在GC時(shí)都采取復(fù)制收集算法,具體算法參考下面的描述喧枷;
Young的默認(rèn)值為 4M虹统,隨堆內(nèi)存增大,約為1/15隧甚,JVM會(huì)根據(jù)情況動(dòng)態(tài)管理其大小變化车荔。
Young里面又分為3個(gè)區(qū)域:
一個(gè)Eden,所有新建對(duì)象都會(huì)存在于 該區(qū)
兩個(gè)Survivor區(qū)戚扳,用來(lái)實(shí)施復(fù)制算法忧便。
Eden區(qū)為對(duì)象通常最初分配到的地方,Survivor區(qū)分為S0和S1兩塊大小相等的區(qū)域帽借。
JVM進(jìn)行Minor GC時(shí)珠增,將Eden中還存活的對(duì)象拷貝到Survivor區(qū)中,還會(huì)將Survivor區(qū)中還存活的對(duì)象拷貝到Old區(qū)中砍艾。在這種GC模式下蒂教,JVM為了提升GC效率, 將Survivor區(qū)分為S0和S1脆荷,這樣就可以將對(duì)象回收和對(duì)象晉升分離開(kāi)來(lái)凝垛。
-XX:NewRatio= 參數(shù)可以設(shè)置Young與Old的大小比例,-server時(shí)默認(rèn)為1:2蜓谋,但實(shí)際上young啟動(dòng)時(shí)遠(yuǎn)低于這個(gè)比率梦皮?如果信不過(guò)JVM,也可以用 -Xmn硬性規(guī)定其大小桃焕,有文檔推薦設(shè)為Heap總大小的1/4剑肯。
-XX:SurvivorRatio= 參數(shù)可以設(shè)置Eden與Survivor的比例,默認(rèn)為32覆旭。Survivio大了會(huì)浪費(fèi)退子,小了的話,會(huì)使一些年輕對(duì)象潛逃到老人區(qū)型将,引起老人區(qū)的不安寂祥, 但這個(gè)參數(shù)對(duì)性能并不太重要。
Old(Tenured):年老代
年輕代的對(duì)象如果能夠挺過(guò)數(shù)次收集七兜,就會(huì)進(jìn)入老人區(qū)丸凭。
老人區(qū)使用標(biāo)記整理算法。因?yàn)?strong>老人區(qū)的對(duì)象都沒(méi)那么容易死的,采用復(fù)制算法就要反復(fù)的復(fù)制對(duì) 象惜犀,很不合算铛碑,只好采用標(biāo)記清理算法,但標(biāo)記清理算法其實(shí)也不輕松虽界,每次都要遍歷區(qū)域內(nèi)所有對(duì)象汽烦,所以還是沒(méi)有免費(fèi)的午餐啊。
-XX:MaxTenuringThreshold= 設(shè)置熬過(guò)年輕代多少次收集后移入老人區(qū)莉御,CMS中默認(rèn)為0撇吞,熬過(guò)第一次GC就轉(zhuǎn)入,可以用-XX:+PrintTenuringDistribution 查看礁叔。
Permanent (Perm):持久代
裝載Class信息等基礎(chǔ)數(shù)據(jù)牍颈,默認(rèn)64M,如果是類很多很多的服務(wù)程序琅关,需要加大其設(shè)置-XX:MaxPermSize=煮岁,否則它滿了之后會(huì)引起 fullgc()或Out of Memory。 注意Spring涣易,Hibernate這類喜歡AOP動(dòng)態(tài)生成類的框架需要更多的持久代內(nèi)存画机。一般情況下,持久代是不會(huì)進(jìn)行GC的新症,除非通過(guò) -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled進(jìn)行強(qiáng)制設(shè)置色罚。
持久代也被成為方法區(qū),方法區(qū)是全局共享的账劲,在一定條件下也會(huì)被GC戳护。
持久代存放JVM加載時(shí)的類型信息:
- 類型基本信息
- 常量池
- 類變量
- 方法信息
- ClassLoader引用
- Class類引用
GC的類型
當(dāng)每個(gè)代滿了之后都會(huì)自動(dòng)促發(fā)collection,各收集器觸發(fā)的條件不一樣瀑焦,當(dāng)然也可以通過(guò)一些參數(shù)進(jìn)行強(qiáng)制設(shè)定腌且。主要分為兩種類型:
- Minor Collection:GC用較高的頻率對(duì)young進(jìn)行掃描和回收,采用復(fù)制算法
- Major Collection:同時(shí)對(duì)Young和Old進(jìn)行內(nèi)存收集榛瓮,也叫Full
GC:因?yàn)槌杀娟P(guān)系對(duì)Old的檢查回收頻率要比Young低很多铺董,采用標(biāo)記清除/標(biāo)記整理算法≠飨可以通過(guò)調(diào)用代碼System.gc()引發(fā)major collection精续,使用-XX:+DisableExplicitGC禁止它,或設(shè)為CMS并發(fā) -XX:+ExplicitGCInvokesConcurrent粹懒。
更為具體的闡述如下:
由于年輕代進(jìn)進(jìn)出出的人多而頻繁重付,所以年輕代的GC也就頻繁一點(diǎn),但涉及范圍也就年輕代這點(diǎn)彈丸之地內(nèi)的對(duì)象凫乖,其特點(diǎn) 就是少量确垫,多次弓颈,但快速,稱之為Minor Collection删掀。當(dāng)年輕代的內(nèi)存使用達(dá)到一定的閥值時(shí)翔冀,Minor Collection就被觸發(fā),Eden及某一Survior space(from space)之內(nèi)存活的的對(duì)象被移到另一個(gè)空的Survior space(to space)中披泪,然后from space和to space角色對(duì)調(diào)纤子。當(dāng)一個(gè)對(duì)象在兩個(gè)survivor space之間移動(dòng)過(guò)一定次數(shù)(達(dá)到預(yù)設(shè)的閥值)時(shí),它就足夠old了款票,夠資格呆在年老代了计福。當(dāng)然,如果survivor space比較小不足以容下所有l(wèi)ive objects時(shí)徽职,部分live objects也會(huì)直接晉升到年老代。
Survior spaces可以看作是Eden和年老代之間的緩沖佩厚,通過(guò)該緩沖可以檢驗(yàn)一個(gè)對(duì)象生命周期是否足夠的長(zhǎng)姆钉,因?yàn)槟承?duì)象雖然逃過(guò)了一次Minor Collection,并不能說(shuō)明其生命周期足夠長(zhǎng)抄瓦,說(shuō)不定在下一次Minor Collection之前就掛了潮瓶。這樣一定程度上確保了進(jìn)入年老代的對(duì)象是貨真價(jià)實(shí)的,減少了年老代空間使用的增長(zhǎng)速度钙姊,也就降低年老代GC的頻率毯辅。
當(dāng) 年老代或者永久代的內(nèi)存使用達(dá)到一定閥值時(shí),一次基于所有代的GC就觸發(fā)了煞额,其特定是涉及范圍廣(量大)思恐,耗費(fèi)的時(shí)間相對(duì)較長(zhǎng)(較慢),但是頻率比較低 (次數(shù)少)膊毁,稱之為Major Collection(Full Collection)胀莹。通常,首先使用針對(duì)年輕代的GC算法進(jìn)行年輕代的GC婚温,然后使用針對(duì)年老代的GC算法對(duì)年老代和永久代進(jìn)行GC描焰。
總結(jié):
- 最小收集
- 較高頻率對(duì)年輕代進(jìn)行掃描、回收
年輕代內(nèi)存使用達(dá)到閥值 --->【觸發(fā)Min GC】 Eden及from space內(nèi)的存活對(duì)象移入to space |
|
|【不足以容納所有對(duì)象時(shí)栅螟,部分移入老人代】
V
---> from/to 角色對(duì)調(diào) --->【一個(gè)對(duì)象移動(dòng)到一定次數(shù)】 移入老人代
GC收集算法
- 復(fù)制 (copying):將堆內(nèi)分成兩個(gè)相同空間荆秦,從根(ThreadLocal的對(duì)象,靜態(tài)對(duì)象)開(kāi)始訪問(wèn) 每一個(gè)關(guān)聯(lián)的活躍對(duì)象力图,將空間A的活躍對(duì)象全部復(fù)制到空間B步绸,然后一次性回收整個(gè)空間A。因?yàn)橹辉L問(wèn)活躍對(duì)象吃媒,將所有活動(dòng)對(duì)象復(fù)制走之后就清空整 個(gè)空間靡努,不用去訪問(wèn)死對(duì)象坪圾,所以遍歷空間的成本較小,但需要巨大的復(fù)制成本和較多的內(nèi)存惑朦∈扌梗可參考如下的示例圖:
- 標(biāo)記清除 (mark-sweep):收集器先從根開(kāi)始訪問(wèn)所有活躍對(duì)象,標(biāo)記為活躍對(duì)象漾月。然后再遍歷一次整個(gè) 內(nèi)存區(qū)域镜硕,把所有沒(méi)有標(biāo)記活躍的對(duì)象進(jìn)行回收處理。該算法遍歷整個(gè)空間的成本較大暫停時(shí)間隨空間大小線性增大耐薯,而且整理后堆里的碎片很多确虱。可參考如下的示 例圖:
- 標(biāo)記整理 (mark-sweep-compact):綜合了上述兩者的做法和優(yōu)點(diǎn)吩蔑,先標(biāo)記活躍對(duì)象钮热,然后將其 合并成較大的內(nèi)存塊≈蚍遥可參考如下的示例圖:
總結(jié):
GC收集算法
1隧期、復(fù)制 (copying)
將堆內(nèi)分成兩個(gè)相同空間,將空間A的活躍對(duì)象全部復(fù)制到空間B赘娄,然后一次性回收空間A
需要拆分
只訪問(wèn)活躍對(duì)象仆潮,所以遍歷空間成本小,復(fù)制成本大
2遣臼、標(biāo)記清除 (mark-sweep)
遍歷第一次訪問(wèn)所有活躍對(duì)象并標(biāo)記
遍歷第二次回收所有未標(biāo)記對(duì)象
** 遍歷成本大性置,碎片多: 空間越大暫停時(shí)間越多 **
3、標(biāo)記整理 (mark-sweep-compact)
compact : 壓緊揍堰、使緊湊
綜合了上述兩者的做法和優(yōu)點(diǎn)鹏浅,標(biāo)記清理后合并活躍對(duì)象成較大的內(nèi)存塊
成本高,但不產(chǎn)生碎片
并行屏歹、并發(fā)的區(qū)別
并行(Parallel)與并發(fā)(Concurrent)僅一字之差篡石,但體現(xiàn)的意思卻完全不同,這可能也是很多同學(xué)非常困惑的地方西采,要想深刻體會(huì)這 其中的差別凰萨,可以多揣摩下上面關(guān)于GC收集器的示例圖;
并行:指多條垃圾收集線程并行械馆,此時(shí)用戶線程是沒(méi)有運(yùn)行的胖眷;
并發(fā):指用戶線程與垃圾收集線程并發(fā)執(zhí)行,程序在繼續(xù)運(yùn)行霹崎,而垃圾收集程序運(yùn)行于另一個(gè)個(gè)CPU上珊搀。
并發(fā)收集一開(kāi)始會(huì)很短暫的停止一次所有線程來(lái)開(kāi)始初始標(biāo)記根對(duì)象,然后標(biāo)記線程與應(yīng)用線程一起并發(fā)運(yùn)行尾菇,最后又很短的暫停一次境析,多線程并行的重新標(biāo) 記之前可能因?yàn)椴l(fā)而漏掉的對(duì)象囚枪,然后就開(kāi)始與應(yīng)用程序并發(fā)的清除過(guò)程±拖可見(jiàn)链沼,最長(zhǎng)的兩個(gè)遍歷過(guò)程都是與應(yīng)用程序并發(fā)執(zhí)行的,比以前的串行算法改進(jìn)太多太 多了E嫱摇@ㄉ住!
串行標(biāo)記清除是等年老代滿了再開(kāi)始收集的曲掰,而并發(fā)收集因?yàn)橐c應(yīng)用程序一起運(yùn)行疾捍,如果滿了才收集,應(yīng)用程序就無(wú)內(nèi)存可用栏妖,所以系統(tǒng)默認(rèn) 68%滿的時(shí)候就開(kāi)始收集乱豆。內(nèi)存已設(shè)得較大,吃內(nèi)存又沒(méi)有這么快的時(shí)候吊趾,可以用-XX:CMSInitiatingOccupancyFraction= 恰當(dāng)增大該比率宛裕。
年輕代的痛
由于對(duì)年輕代的復(fù)制收集,依然必須停止所有應(yīng)用程序線程趾徽,原理如此,只能靠多CPU翰守,多收集線程并發(fā)來(lái)提高收集速度孵奶,但除非你的Server獨(dú)占整臺(tái)服務(wù)器,否則如果服務(wù)器上本身還有很多其他線程時(shí)蜡峰,切換起來(lái)速度就..... 所以了袁,搞到最后,暫停時(shí)間的瓶頸就落在了年輕代的復(fù)制算法上湿颅。
因 此Young的大小設(shè)置挺重要的 载绿,大點(diǎn)就不用頻繁GC,而且增大GC的間隔后油航,可以讓多點(diǎn)對(duì)象自己死掉而不用復(fù)制了崭庸。 但Young增大時(shí),GC造成的停頓時(shí)間攀升得非骋昵簦恐怖怕享,據(jù)某人的測(cè)試結(jié)果顯示:默認(rèn)8M的Young,只需要幾毫秒的時(shí)間镰踏,64M就升到90毫秒函筋,而升 到256M時(shí),就要到300毫秒了奠伪,峰值還會(huì)攀到恐怖的800ms跌帐。誰(shuí)叫復(fù)制算法首懈,要等Young滿了才開(kāi)始收集,開(kāi)始收集就要停止所有線程呢谨敛。