1. 程式人生 > >判斷Java 物件例項是否死亡

判斷Java 物件例項是否死亡

垃圾收集器與記憶體分配策略參考目錄:

1.判斷Java 物件例項是否死亡 
2. Java 中的四種引用 
3.垃圾收集演算法 
4. Java9中的GC 調優 
5.記憶體分配與回收策略
在進入主題之前,我們要先知道執行時資料區域都是有哪些塊記憶體需要進行垃圾回收。 
    程式計數器、虛擬機器棧、本地方法棧、3個區域都是隨著執行緒生而生,隨著執行緒滅而滅的;棧中的棧幀隨著方法的進入和退出有條不紊的執行著進棧和出棧的操作。每一個棧幀中分配多大的記憶體基基本上在類結構確定下來後就已知了,因此這幾個區域的記憶體分配和回收都具備確定性,在這幾個區域就不需要過多的考慮垃圾回收的問題,因為 方法結束或者執行緒結束時,記憶體自然就跟著回收了。而Java 堆和方法區不一樣,一個介面中方的多個實現類需要的記憶體可能不一樣,一個方法的多個分支需要的記憶體可能有也不一樣,我們只有在程式處於執行期間才知道會建立哪些物件,這部分記憶體的分配和回收都是動態的,垃圾回收機制所關注的也是這部分記憶體。

在Java 堆裡幾乎放著所有Java 物件的例項,垃圾收集器在對堆進行回收之前,第一件事就是要確定哪些物件是存活的,哪些物件已經”死去”。 
    在進行GC Root 之前,我們先來了解一個判斷物件是否存活的演算法:引用計數法。該演算法的是這樣的:給物件中新增一個引用計數器,每當有一個地方引用它時,計數器值就加1 ;當引用失效時,計數器值就減1 ;任何時刻該計數器的值為0 時,就說明該物件是不可能再被使用的。 
    引用計數演算法(Reference Counting)的實現很簡單,效率也很高,在大部分情況下都是一個不錯的演算法。但是目前Hot Spot 虛擬機器並沒有使用這種演算法來管理記憶體,因為這種演算法很難解決物件之間相互引用的問題。下面就是一個簡單的例子,建立了兩個物件,這兩個物件都相互引用著對方,導致他們的引用計數器都不為0,所以引用計數法就不能通知GC 收集器收集他們,但這兩個物件除了相互引用之外沒有任何其他的用途。我列印了GC 日誌以提供分析,為了防止大物件被直接分配到老年代,我把大物件分配到老年代的記憶體設定成了3m大小。

public class ReferenceCountingGC {

    public Object instance = null;
    private static final int _1MB = 1024*1024;
    private byte[] bigSize = new byte[2 * _1MB];

    public static void main(String[] args) {

        ReferenceCountingGC obj1 = new ReferenceCountingGC();
        ReferenceCountingGC obj2 = new ReferenceCountingGC();
        obj1.instance = obj2;
        obj2.instance = obj1;

        System.gc();
    }
}
 


    由上面的圖(如果上圖看不清楚你可右鍵在新標籤頁面檢視圖片)可以看出在進行垃圾回首之前新生代被佔用了4744 K 大小,在GC 之後新生代的記憶體就被釋放了。因此這也可以說明虛擬機器並不是通過引用計數法判斷物件是否能存活的。

可達性分析演算法:

目前的主流程式語言的實現中,都是通過可達性分析(Reachability Analysis)來判定物件是否存活的。這個演算法的基本思路就是一系列的稱為”GC Roots” 的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑被稱為引用鏈(Reference Chain),當一個物件到GC Roots 沒有任何引用鏈相連時,則證明這個物件是不可用的(圖片來自百度搜索)。如圖所示,object5、object6、object7 雖然互有關聯,但是它們到GC Roots 是不可達的,所以它們將被判定為可回收的物件。 
 
在Java 語言中可以作為GC Roots 的物件包括下面這幾種:

虛擬機器棧中引用的物件
本地方法棧中引用的物件
堆中類靜態屬性引用的物件
--------------------- 
作者:留蘭香丶 
來源:CSDN 
原文:https://blog.csdn.net/codejas/article/details/78663217 
版權宣告:本文為博主原創文章,轉載請附上博文連結!