欧美一区二区三区,国内熟女精品熟女A片视频小说,日本av网,小鲜肉男男GAY做受XXX网站

如何判定一個對象是否是可回收的對象

榮姿康2年前23瀏覽0評論

如何判定一個對象是否是可回收的對象?

我來回答一個吧。

什么叫做垃圾?在GC中如何來判斷一個對象是否為垃圾呢?

reference count(引用計數法)

主要是查看該對象是否還有引用指向它,如果有則說明該對象不是垃圾,反之則為垃圾。

具體就是給一個對象腦門上標一個數字用來記錄有多少個引用指向了該對象,當這個數字記錄為0時,那就表示這個對象已經沒有引用指向它了,那么這個對象就變成了垃圾。

通過上圖可以看到:

在階段一時,這個對象腦門的數字是3,表示有三個引用指向它,所以這時它不是垃圾;

到了階段二時,對象腦門上的數字變成了2,表示有兩個引用指向它,這時它也不是垃圾;

依此,到了階段三數字變成了1,還有一個引用指向它,這時還不是垃圾,直到階段四,這個數字變成了0,沒有引用再指向它了,這時候這個對象就變成了垃圾,垃圾回收器就可以將它回收了。

引用計數法存在的問題

當出現循環引用時,如下圖:

這時候,A、B、C三個對象各自指向這個循環中的另一個對象,也就是這三個對象的腦門上都有數字1,但是實際上又沒有其他的對象指向他們三個,這種情況下,A、B、C三個對象都應該屬于垃圾,需要被垃圾回收器清理掉。

但問題是按照引用計數法來判斷的話,這三個對象腦門上的數字都不是0,所以垃圾回收器就無法找到這三個對象來清理掉,需要解決這個問題,JVM引入了另外一種算法來判斷對象是否為垃圾-根可達算法(Root Searching)。

根可達算法(Root Searching)

名詞解析:

根可達算法:是從根上對象開始搜索的算法。

線程棧變量:一個main方法開始運行,main線程棧中的變量調用了其他變量,main棧中的方法訪問到的對象叫根對象。

靜態變量:T.class對靜態變量初始化時能訪問到的對象叫根對象。

常量池:如果一個class能夠用其他class的對象叫根對象。

JNI指針:如果調用本地方法運用到的對象叫根對象。

根對象:當一個程序啟動的時候需要用到的對象叫根對象。

根可達算法意思就是從根上開始搜,Java程序是從一個main方法開始運行的,一個main方法會開啟一個線程,這個線程就會有線程棧,里面就會有main棧幀。從main棧幀里面創建出來的對象都是根對象,當然main方法里面調用了別的方法,那別的方法也是我們引用到的,這些都是有引用的對象,但是從main開始的這個棧幀里的對象都叫做根對象。

另外一個是靜態變量,一個class它有一個靜態的變量,class的被load到內存之后就會對靜態變量進行初始化,所以靜態變量訪問到的對象也是根對象。

還有就是常量池,如果你這個class會用到其他class的那些類的對象,那這些對象也是根對象。

最后是JNI,JNI指的是如果你調用了C和C++寫的那些本地方法所用的的對象也是根對象。

如上圖所示,對象一、二、三、四、五均是存在根對象的引用,對象五、六之間是我們上面所提到的循環引用,對象八不存在引用,故對象六、七、八是垃圾。

三色標記算法原理GC Algorithms(常見的垃圾回收算法) 到目前為止一共有三種。

Mark-Sweep(標記清除)Copying(拷貝)Mark-Compact(標記壓縮或標記整理)1.Mark-Sweep(標記清除)

將可回收的垃圾對象進行標記定位,清除被標記的對象即可,將垃圾位變為可用位。

這個算法非常簡單,但存在缺點,長時間運行會存在大量的內存碎片。

何為碎片?

由上述得知,每一小塊可回收內存均需要標記后單獨清除,在業務量較大,頻繁更新數據的情況下,會有個別的“碎片”長期存在于內存中不被使用,占用資源空間。大量的碎片就會造成查詢效率極其低下,所以我們就需要進行處理。

2.Copying(拷貝)

直接把內存一分為二,分開之后把有用的對象拷貝到下面綠色的區域,拷貝完之后,直接把上面的全部清掉即可。

由于每次拷貝完成,所有的內存空間都是排列在一起的,因此Copying(拷貝)算法就可以解決上面提到的碎片化問題,但是會造成內存空間的浪費,每次僅可以使用一般的內存空間進行操作。

2.Mark-Compact(標記壓縮或標記整理)

把有用的對象全部壓縮到整塊內存的最前面,剩下的大塊空間就全部清出來了,既不存在碎片化問題,也不浪費空間。

標記壓縮存在的問題是效率過低,由于每次在壓縮之間都需要計算空間,導致回收的效率大大降低。

垃圾回收器的制定原則上述三種標記算法可謂是各有利弊,因此在實際應用中,一個垃圾回收器的制定是綜合了上述三種算法。

新生代分為:

eden(伊甸)默認比例8:是我們剛剛新new出來對象之后就往里面扔的內存區域。survivor區又分為兩個區域:

survivor0 默認比例1:是對象被回收一次之后跑到這個區域的,這里面由于裝的對象不同,所以采取的算法也不同。survivor1 默認比例1新生代存活的對象特別少,死去的對象特別多,適合使用Copying算法。

old老年代

tenured(終身)老年代活著的對象特別多,適用于Mark-Compact算法。

如上圖,我們將新誕生的對象存放在新生代里。如果新誕生的對象經歷了數次垃圾回收仍然沒有被回收掉(即每經歷一次垃圾回收,該對象年齡 +1,即 age++),當 age 到達一定數值,將該對象置于老年代中進行特殊處理。

我們來看一個對象從出生到消亡的過程,通過上圖你應該就明白這個對象是怎么進行GC過程的。

一個對象產生之后首先在棧上分配,棧上如果分配不下就會進入伊甸區,伊甸區經過一次垃圾回收之后進入survivor區,survivor區再經過一次回收后又進入另一個survivor區,什么時候對象的年齡夠了就會進入Old區,這就是整個對象的一個邏輯移動過程。

幾個問題1.新生代里面對象的 age 要取值多少?

這個即是我們進行 JVM 調優所需要的自行調整的,根據項目需求來設置。

同時對于年齡的設置,與具體所使用的 GC 息息相關。

參數:

-XX:Maxtenuringthreshholdbr

如果之前沒有對 GC 進行調整或調優的話,默認使用的GC 是 PS+PO(Parallel Scavenge+ParallelOld),默認年齡為 15。如果進行調整之后所使用的 GC 是 CMS,那 age 就是 6。如果使用的 GC 是 G1 的話,則就徹底與 age 無關,因為該 GC 不分代。關于GC的分類,在下篇文章中會詳細講到。

2.為什么年輕代用 Copying(拷貝)算法?

首先我們先考慮Mark-Sweep(標記清除)和 Mark-Compact(標記壓縮或標記整理),上面我們已經說到,這兩種 GC 算法的缺點分別是:產生碎片化問題、內存回收效率低。

程序產生對象后,該對象很可能會在很短的時間內被回收,根據統計,一次垃圾回收可以回收掉 90% 的對象。在這樣的情況下,使用 Mark-Sweep(標記清除)和 Mark-Compact(標記壓縮或標記整理)效率就太低了,會造成伊甸園區很快爆滿或者大規模碎片化,而新產生對象在放進去時效率就會大大降低。

所以在 JVM 設計中,要求年輕代的算法效率是特別高、特別快的。而 Copying(拷貝)算法的效率是最高的,但是浪費了年輕代中至少一半的內存空間。

那我們既要利用好 Copying(拷貝)算法效率高的優勢,又要盡量避免內存浪費的問題,怎么解決?

Copying(拷貝)算法在年輕代中的具體應用

第一次垃圾回收:首先將 10% 的幸存對象拷貝到第一個 survivor 中,即 s0 中,然后將整個伊甸區進行清除,這時所有有用對象都存放在 s0 中。

第二次垃圾回收:將伊甸區中有用的對象拷貝到另一個 survivor 中,即 s1 中,再將之前 s0 中的有用對象拷貝到 s1 中,對伊甸園區與第一個 s0 進行垃圾回收。這時所有有用的對象存放在 s1 中。

第三次垃圾回收:再次利用 s0,將之前存活的對象與伊甸區中產生的新對象存放在 s0 中,對伊甸園區與 s1 進行二垃圾回收。

第n次垃圾回收:如此循環往復利用新生代中的伊甸園區與 survivor 區即可。

好了以上就是關于GC如何判定對象是否為可回收的垃圾對象的方法,又3000字了,如果覺得對您有所幫助,歡迎點贊收藏轉發,同時也歡迎關注我的微信公眾號【Seven的代碼實驗室】獲取更多最新文章動態,不定期有項目福利分享。

java 對象回收,如何判定一個對象是否是可回收的對象