JVM系列之內存分配與回收策略

前言

經過前面幾篇文章,我們已經對JVM虛擬機有了個初步認識,也瞭解了各區域應用哪些收集器,以及每個收集器用哪些收集算法,但是在這一系列過程中,其實就是一個對象的由生到死的過程,那麼在這過程中對象是怎麼處理它的生命週期的呢,今天我們就來介紹下對象在內存中的分配和回收分配給對象的內存,那麼,讓我們帶着愉快的週末心情來開啓今天的JVM之旅吧。

 

內存分配

內存分配,通俗講就是對象在堆上分配:對象主要分配在新生代的Eden區上,如果啓動了本地線程分配緩衝,將按線程優先在TLAB上分配。少數對象也可能直接分配在老年代中,下面我們將具體講解:

 

TLAB解釋:全稱爲Thread Local Allocation Buffer即線程本地分配緩存,從名稱上看是一個線程專用的內存分配區域,是爲了加速對象分配而生的。每一個線程都會產生一個TLAB,該線程獨享的工作區域,java虛擬機使用這種TLAB區來避免多線程衝突的問題,提高了對象分配的效率。TLAB空間一般不會太大,當大對象無法在TLAB上分配時,則會直接分配到堆上;

 

對象優先在Eden分配:大多數情況下,對象在新手代Eden區中分配,當Eden區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC,虛擬機提供-XX:+PrintGCDetails配置收集器日誌參數,通知虛擬機在發生垃圾收集時打印內存回收日誌,並且在進程退出時輸出當前內存各區域分配情況。

 

大對象直接進入老年代:大對象,指的就是需要大量連續內存空間的Java對象,比較典型的大對象就是那種很長的字符串以及數組,經常出現大對象容易導致當內存空間還有很多的時候就提前觸發收集器收集以獲取足夠連續的內存空間;

虛擬機提供一個可控制的參數:-XX:PretenureSizeThreshold來設置對象直接進入老年代的閾值,當對象大於這個值直接進入老年代,這樣做是爲了避免在Eden區和Survivor區之間發生大量的內存複製。

 

長期存活的對象將直接進入老年代:新生代的對象每個對象設置一個計數器(age),出生時首先分配在Eden區,如果經過第一次Minor GC還存活,並且能被Survivor接納的對象,則進入Survivor空間中,並且對象年齡設爲1,對於在Survivor空間中的對象,每經歷一次Minor GC,年齡加+1,當它的年齡增加到一定程度(默認爲15),就將晉升到老年代;這就好比我們人剛形成時在母親體內(Eden),出生後(Survivor)每過一年(Minor GC),年齡就+1,到了一定程度(18歲)就成年了;

虛擬機提供一個可控制的參數:-XX:MaxTenuringThreshold來控制新生代晉升到老年代的閾值(默認爲15)。

 

擴展

Minor GC (新生代GC):發生在新生代的垃圾收集動作,因爲Java對象大多數都具備朝生夕滅的特點,所以Minor GC非常頻繁,一般回收速度也很快;

Full GC(老年代GC):又稱Major GC,發生在老年代的垃圾收集,通常發生Full GC意味着至少發生了一次Minor GC(直接出生早老年代的除外),Full GC因爲都是大對象回收,所以通常速度要比Minor GC慢很多;

 

回收策略

對象年齡動態判定:爲了更好的適應不同程序的內存狀況,虛擬機並不是永遠要求對象年齡必須達到XX:MaxTenuringThreshold才能晉升到老年代,如果在Survivor空間中,相同年齡的所有對象大小的和大於Survivor空間的一半,就會將Survivor空間中年齡大於或者等於該年齡的對象直接進入老年代;

 

空間分配擔保:什麼是空間分配擔保?在發生Minor GC之前,虛擬機會檢查老年代最大的連續空間是否大於新生代所有對象總空間,如果成立那麼就能確保此次GC是安全的。如果不成立,即Minor GC不是安全的,那麼虛擬機就會查看HandlePromotionFailure設置的值是否允許擔保失敗(不安全的Minor GC),

如果允許,那麼檢查老年代最大可用連續空間是否大於歷次晉升老年對象的平均大小,如果大於則繼續Minor GC(儘管是有風險的),如果不允許或者小於平均大小,則觸發一次Full GC,流程圖如下:

以上就是今天內存分配和回收策略的所有內容,至此我們也就罷內存管理相關知識講完了!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章