新生代和老年代
對象在被實例化之后,都是屬於新生代。
大部分新生代的生命周期都是及其短暫的,例如在一個方法中創建的對象會隨着方法執行完畢,棧空間的棧幀出棧后而失去引用。
而有一些對象確實會長期存活在堆內存的,比如被Static引用的對象。這種對象不會輕易的被垃圾回收器回收。
所以JVM會將堆內存分為兩個區域,一個年輕代,一個老年代。
其中年輕代,顧名思義,就是創建和使用完之后立馬就要被回收的對象放在里面。然后老年帶呢,就是把一些會長期存活的對象放在里面。
為什么要分成新生代和老年代
很多人都會有一個疑問,為什么要划分兩個區域呢?
因為這個垃圾回收有關,對於年輕代的對象,他們的特點是很快就會被回收,所以需要使用一種垃圾回收算法。
而對於老年代而言,里面的大部分對象可能都會長期存活,那么使用新生代的回收算法放在這里就可能並不是那么的合適。需要有着自己的一套回收算法。
什么是永久代
很簡單,JVM里的永久代其實就是我們之前說的方法區
所謂的永久代,你可以認為是存放一些類的信息,在上一個章節我們知道我們生成的.class就是存放在這個區域的。一般情況下,我們對於jvm調優都是對新生代和老年代進行調優。一般而言永久代保持默認配置就可以了。
是不是意味着我們就不需要關注永久代?
肯定不是的。因為要存儲類的相關信息,所以對於動態生成類的情況比較容易出現永久代的內存溢出。最典型的場景就是,在 jsp 頁面比較多的情況,容易出現永久代內存溢出。
如何判斷一個對象是否是垃圾
我們知道,當一個對象被創建出來的時候,比如說在一個方法中創建一個對象,當該方法執行完畢后,就沒有引用指向這個對象了,這個對象就會變成垃圾對象。
這僅僅是一種情況。
到低哪些對象是辣雞,哪些對象不是?
JVM中使用了一種可達性分析算法來判定哪些對象是可以被回收的。這個算法的核心就是看這個對象有誰在引用它,然后一層一層的往上判斷,看是否被GC roots所引用。
在java中,可作為GC Roots的對象有:
1.虛擬機棧(棧幀中的本地變量表)中引用的對象(也就是我們前面提到在方法中創建的對象);
2.方法區中的類靜態屬性引用的對象;
3.方法區中常量引用的對象;
4.本地方法棧中JNI(即一般說的Native方法)中引用的對象
當一個對象沒有被上述所引用,那么這個對象就可以被認為是垃圾對象了。
總之記住一句話,只要你的對象被方法的具備變量,類的靜態變量給引用了,就不會回收他。
java中對象不同的引用關系。
1、強引用
就是被GC roots所直接引用的對象。只要是強引用關系,那么垃圾回收器是絕對不會回收這個對象的。
2、軟引用
public class test{
public static SoftReference<ReplicaManager> manager =
new SoftReference<ReplicaManager>(new Replicamanager)
}
/**如上訴代碼,實例對象被“SoftReference” 軟引用類型的對象包裝起來了,那么這個對象的引用就是軟引用。
正常情況下是不會回收軟引用對象的,但是如果你進行垃圾回收后,返現內存還是不夠存放新的對象的時候,這個時候就會吧軟引用的對象給回收掉
**/
3、弱引用
這個相對前兩中使用的很少,與軟引用類時,當一個實例對象被“WeakReference”弱引用的對象包裝起來的時候,那么這個對象就是弱引用。
弱引用的生命周期就存在下一次垃圾回收之前,也就是說下一次垃圾回收會回收掉弱引用的對象。
4、虛引用
最弱雞的一種引用,我感覺沒什么作用。一個對象是不是虛引用對他本生的生命周期沒有影響。該什么時候回收就什么時候回收。
設置他唯一的目的就是被回收的時候會得到一個系統通知,一般來說沒什么卵用
finalize()方法的作用
到這里,我相信你應該清楚了哪些對象會被回收哪些對象不會被回收。
如果一個對象被GC Roots所引用,但是!!他如果是軟引用或弱引用,那么也是可能會被辣雞回收器給回收掉的。
在垃圾對象被回收的時候,會調用Object對象的finalize()方法。
我們來模擬一個辣雞的自我救贖代碼。
public class A{
public static A instance;
@Override
protected void finalize() throws Throwable{
A.instance = this;
}
}
當對象被回收的時候,調用finalize方法,重新將GC Roots的變量引用指向自己,那么就不會被回收了。
這個基本上沒人用,感覺就是一個sb才會寫的代碼。寫出來就是給大家說出這個細節。
新生代對象是如何變成老年代對象的
長期存活的對象會多次躲過垃圾回收
躲過多少次Minor GC而沒有被回收掉,我們就認為這個對象的年齡有幾歲了,默認情況下,當一個對象10多歲的時候,就認為他是一個老人了。需要被轉移到老年代去。
想想也正常,新生代的競爭壓力這么大,老年對象還是早點去老年代比較好不與年輕人競爭資源。
特別大的超大對象直接不經過新生代就進入老年代
年輕代的的競爭本來就很大,你還要占用那么多資源,不行不行,你還是去老年代吧。
動態年齡判斷機制
這一部分需要結合垃圾回收器的算法來講,就是復制算法。我們在后面的章節會講到,這里大概介紹一下。
我們一般會將新生代分為三個區域,一個Eden,兩個Survivor。比例8:1:1.
生成的對象默認在eden區域。當發生一次Minor GC后,會將存活的對象復制到其中一個Survivor區域。當下一次GC后又會將存活的對象復制到另一塊Survivor。這么做的好處是減少內存碎片。
當我們發生一次GC后,將存活對象放到其中一塊survivor區域。發現其中的1歲,2歲,3歲的對象年齡加起來內存超過survivor區域的一半,就會把4歲以及4歲以上的對想轉移到老年代。
GC后survivor區域存放不下
這個沒辦法,GC后都還存放不下一般來說要不是訪問量激增,要么就是優化的不到位,所以只好將這寫對象轉移到老年代。
空間擔保機制
在發生minor gc之前,虛擬機會檢測 : 老年代最大可用的連續空間>新生代all對象總空間?
1、滿足,minor gc是安全的,可以進行minor gc。
2、不滿足,虛擬機查看HandlePromotionFailure參數:
(1)為true,允許擔保失敗,會繼續檢測老年代最大可用的連續空間>歷次晉升到老年代對象的平均大小。若大於,將嘗試進行一次minor gc,若失敗,則重新進行一次full gc。
(2)為false,則不允許冒險,要進行full gc(對老年代進行gc)。