目的
主要介紹java 垃圾回收如何與hotspot JVM配合使用的基礎知識靶溜。在了解了垃圾收集器的功能后开瞭,了解Visual VM監(jiān)控垃圾收集的過程。
探索JVM架構
Hotspot 架構
HotSpot JVM 的架構支持強大的特性和能力基礎罩息,并支持實現(xiàn)高性能和大規(guī)泥拖辏可擴展性的能力。例如瓷炮,HotSpot JVM JIT 編譯器生成動態(tài)優(yōu)化葱色。換句話說,它們在 Java 應用程序運行時做出優(yōu)化決策崭别,并生成針對底層系統(tǒng)架構的高性能本機機器指令冬筒。此外恐锣,通過其運行時環(huán)境和多線程垃圾收集器的成熟演變和持續(xù)工程茅主,HotSpot JVM 即使在最大的可用計算機系統(tǒng)上也能產(chǎn)生高可擴展性。
關鍵組件
在調(diào)整性能時土榴,JVM 有三個組件是重點诀姚。堆是存儲對象數(shù)據(jù)的位置。然后玷禽,此區(qū)域由啟動時選擇的垃圾回收器管理赫段。大多數(shù)優(yōu)化選項都與調(diào)整堆大小和根據(jù)您的情況選擇最合適的垃圾回收器有關。JIT 編譯器對性能也有很大的影響矢赁,但很少需要使用較新版本的 JVM 進行調(diào)優(yōu)糯笙。
垃圾回收
什么是自動垃圾回收?
自動垃圾回收是查看堆內(nèi)存撩银、識別哪些對象正在使用以及哪些對象未使用以及刪除未使用對象的過程给涕。正在使用的對象或引用的對象意味著程序的某些部分仍保留指向該對象的指針。未使用的對象或未引用的對象不再被程序的任何部分引用。因此够庙,可以回收未引用對象使用的內(nèi)存恭应。
在像C這樣的編程語言中,分配和釋放內(nèi)存是一個手動過程耘眨。在 Java 中昼榛,釋放內(nèi)存的過程由垃圾回收器自動處理√弈眩基本過程分為以下幾步:
1. 標記
標記的作用是垃圾回收器識別哪些內(nèi)存正在使用和哪些未使用胆屿。
引用的對象以藍色顯示;未引用的對象以金黃色顯示偶宫。
在標記階段莺掠,所有對象將被掃描,并被標記读宙。如果必須掃描系統(tǒng)中的所有對象彻秆,這可能是一個非常耗時的過程。
2. 正常刪除
正常刪除會刪除未引用的對象结闸,留下引用的對象和指向可用空間的指針唇兑。
內(nèi)存分配器保存對可用空間塊的引用,可以在其中分配新對象桦锄。
使用壓縮進行刪除
若要進一步提高性能扎附,除了刪除未引用的對象外,還可以壓縮其余引用的對象结耀。通過將引用的對象移動到一起留夜,這使得新的內(nèi)存分配更加容易和快捷。
為什么要進行按“代”進行垃圾回收图甜?
通過前面的描述碍粥,我們可以了解到標記和壓縮jvm中所有的對象是低效的、耗時的黑毅。隨著分配的對象越來越多嚼摩,對象的列表會越來越長,從而導致垃圾回收的時間越來越長矿瘦。然而通過對應用程序的實際分析發(fā)現(xiàn)枕面,大部分對象的使用時間都是短暫的。
下面是此類數(shù)據(jù)的示例缚去,Y 軸顯示分配的字節(jié)數(shù)潮秘,X 訪問顯示隨時間分配的字節(jié)數(shù)。
如您縮減易结,隨著時間的推移枕荞,保留分配的對象越來越少稠通。所以實際上,大多數(shù)的對象壽命非常短买猖,正如圖左側的較高值所示改橘。
JVM年齡代
堆空間包括:年輕代、年老代玉控、永生代飞主。
年輕代(eden)
是分配和老化所有新生對象的地方。當年輕代被填滿時高诺,會導致小的垃圾回收碌识。
S0
、S1
是兩個大小相同的內(nèi)存區(qū)域虱而,主要存放每次垃圾回收后eden區(qū)存活的對象筏餐,作為對象從eden過渡到年老代的緩沖地帶。
年老代
主要存放生命周期長的存活對象牡拇。通常為年輕代設置閾值魁瞪,當達到該年齡后,對象將被移動到年老代惠呼。
永生代
主要存放所有已加載的類信息导俘、方法信息、常量池等剔蹋÷帽。可通過-XX:PermSize和-XX:MaxPermSize來指定持久帶初始化值和最大值。Permanent Space并不等同于方法區(qū)泣崩,只不過是Hotspot JVM用Permanent Space來實現(xiàn)方法區(qū)而已少梁,有些虛擬機沒有Permanent Space而用其他機制來實現(xiàn)方法區(qū)。
對象分配和老化的過程
-
任何新對象都會被分配到eden區(qū)矫付,S0凯沪、S1開始都是空的
-
當eden區(qū)被填滿時,將會觸發(fā)一次小的垃圾回收
-
此時被引用的對象將會被移動到S0技即,未被引用的對象將被刪除
-
在下一次的Minor GC中著洼,Eden區(qū)的情況和上面一致,沒有引用的對象被回收而叼,存活的對象被復制到survivor區(qū)。然而在survivor區(qū)豹悬,S0的所有的數(shù)據(jù)都被復制到S1葵陵,需要注意的是,在上次minor GC過程中移動到S0中的兩個對象在復制到S1后其年齡要加1瞻佛。此時Eden區(qū)S0區(qū)被清空脱篙,所有存活的數(shù)據(jù)都復制到了S1區(qū)娇钱,并且S1區(qū)存在著年齡不一樣的對象
-
再下一次Minor GC則重復這個過程,這一次survivor的兩個區(qū)對換绊困,存活的對象被復制到S0文搂,存活的對象年齡加1,Eden區(qū)和另一個survivor區(qū)被清空秤朗。
-
下面演示一下Promotion過程煤蹭,再經(jīng)過幾次Minor GC之后,當存活對象的年齡達到一個閾值之后(可通過參數(shù)配置取视,默認是8)硝皂,就會被從年輕代Promotion到老年代。
隨著Minor GC一次又一次的進行作谭,不斷會有新的對象被promote到老年代稽物。
隨著MinorGC一次又一次的進行,不斷會有新的對象被promote到老年代折欠。-
上面基本上覆蓋了整個年輕代所有的回收過程贝或。最終,Major GC將會在老年代發(fā)生锐秦,老年代的空間將會被清除和壓縮傀缩。
總結
從上面的過程可以看出,Eden區(qū)是連續(xù)的空間农猬,且Survivor總有一個為空赡艰。經(jīng)過一次GC和復制,一個Survivor中保存著當前還活著的對象斤葱,而Eden區(qū)和另一個Survivor區(qū)的內(nèi)容都不再需要了慷垮,可以直接清空,到下一次GC時揍堕,兩個Survivor的角色再互換料身。因此,這種方式分配內(nèi)存和清理內(nèi)存的效率都極高衩茸,這種垃圾回收的方式就是著名的“停止-復制(Stop-and-copy)”清理法(將Eden區(qū)和一個Survivor中仍然存活的對象拷貝到另一個Survivor中)芹血,這不代表著停止復制清理法很高效,其實楞慈,它也只在這種情況下(基于大部分對象存活周期很短的事實)高效幔烛,如果在老年代采用停止復制,則是非常不合適的囊蓝。老年代存儲的對象比年輕代多得多饿悬,而且不乏大對象,對老年代進行內(nèi)存清理時聚霜,如果使用停止-復制算法狡恬,則相當?shù)托е槭濉R话悖夏甏玫乃惴ㄊ菢擞?壓縮算法弟劲,即:標記出仍然存活的對象(存在引用的)祷安,將所有存活的對象向一端移動,以保證內(nèi)存的連續(xù)兔乞。在發(fā)生Minor GC時汇鞭,虛擬機會檢查每次晉升進入老年代的大小是否大于老年代的剩余空間大小,如果大于报嵌,則直接觸發(fā)一次Full GC虱咧,否則,就查看是否設置了-XX:+HandlePromotionFailure(允許擔保失斆)腕巡,如果允許,則只會進行MinorGC血筑,此時可以容忍內(nèi)存分配失敾娉痢;如果不允許豺总,則仍然進行Full GC(這代表著如果設置-XX:+Handle PromotionFailure车伞,則觸發(fā)MinorGC就會同時觸發(fā)Full GC,哪怕老年代還有很多內(nèi)存喻喳,所以另玖,最好不要這樣做)。
關于方法區(qū)(共享內(nèi)存區(qū)的持久代)即永久代的回收表伦,永久代的回收有兩種:常量池中的常量谦去,無用的類信息,常量的回收很簡單蹦哼,沒有引用了就可以被回收鳄哭。對于無用的類進行回收,必須保證3點:
- 類的所有實例都已經(jīng)被回收
- 加載類的ClassLoader已經(jīng)被回收
- 類對象的Class對象沒有被引用(即沒有通過反射引用該類的地方)
永久代的回收并不是必須的纲熏,可以通過參數(shù)來設置是否對類進行回收妆丘。