java 垃圾回收總結(1)


以前看過很多次關於垃圾回收相關的文章,都只是看過就忘記了,沒有好好的整理一下,發現寫文章可以強化自己的記憶。

 

java與C,c++有很大的不同就是java語言開發者不需要關注內存信息,不會顯式的直接操作內存,而是通過jvm虛擬機來實現。

java虛擬機運行的時候內存分配圖如下圖:

1

 

jvm虛擬機棧:一個是線程獨有的,每次啟動一個線程,就創建一個jvm虛擬機棧,線程退出的時候就銷毀。這里面主要保存線程本地變量名和局部變量值。

本地方法棧: 調用本地jni方法的時候而創建的。這里分配的jvm之外的內存空間。方法調用結束之后銷毀。

pc寄存器 : 這個保存線程當前執行的字節碼指令

堆:主要保存創建的對象。

方法區:保存class相關的信息。主要是class的一個內存結構信息

常量池:方法區的一部分,主要保存class內存結構中常量值 例如String值,public static final 類型的值

 

我們這里說的垃圾回收,主要是java虛擬機對堆內存區域的回收。

 

1 首先的問題是:jvm如何知道那些對象需要回收 ?

目前有兩種算法

  • 引用計數法

每個對象上都有一個引用計數,對象每被引用一次,引用計數器就+1,對象引用被釋放,引用計數器-1,直到對象的引用計數為0,對象就標識可以回收

這個可以用數據算法中的圖形表示,對象A-對象B-對象C 都有引用,所以不會被回收,對象B由於沒有被引用,沒有路徑可以達到對象B,對象B的引用計數就就是0,對象B就會被回收。

 

 2

但是這個算法有明顯的缺陷,對於循環引用的情況下,循環引用的對象就不會被回收。例如下圖:對象A,對象B 循環引用,沒有其他的對象引用A和B,則A和B 都不會被回收。

 3

  • root搜索算法

這種算法目前定義了幾個root,也就是這幾個對象是jvm虛擬機不會被回收的對象,所以這些對象引用的對象都是在使用中的對象,這些對象未使用的對象就是即將要被回收的對象。簡單就是說:如果對象能夠達到root,就不會被回收,如果對象不能夠達到root,就會被回收。

如下圖:對象D訪問不到根對象,所以就會被回收

4

以下對象會被認為是root對象:

  • 被啟動類(bootstrap加載器)加載的類和創建的對象
  • jvm運行時方法區類靜態變量(static)引用的對象
  • jvm運行時方法去常量池引用的對象
  • jvm當前運行線程中的虛擬機棧變量表引用的對象
  • 本地方法棧中(jni)引用的對象

由於這種算法即使存在互相引用的對象,但如果這兩個對象無法訪問到根對象,還是會被回收。如下圖:對象C和對象D互相引用,但是由於無法訪問根,所以會被回收。

5

jvm在確定是否回收的對象的時候采用的是root搜索算法來實現。

在root搜索算法的里面,我們說的引用這里都指定的是強引用關系。所謂強引用關系,就是通過用new 方式創建的對象,並且顯示關聯的對象

Object obj = new Object();

以上就是代表的是強引用關系,變量obj 強引用了 Object的一個對象。

java里面有四種應用關系,從強到弱分別為:

Strong Reference(強引用) –>Weak Reference (弱引用) -> Soft Reference(軟引用) – > Phantom Reference(引用)

 

Strong Reference : 只有在引用對象root不可達的情況下才會標識為可回收,垃圾回收才可能進行回收

Weak Reference :即使在root算法中 其引用的對象root可達到,但是如果jvm堆內存 不夠的時候,還是會被回收。

Soft Reference : 無論其引用的對象是否root可達,在響應內存需要時,由垃圾回收判斷是否需要回收。

Phantom Reference :在回收器確定其指示對象可另外回收之后,被加入垃圾回收隊列.

 

下面可以看一個測試

public class ReferenceTest {

    public static final Map<Integer, Reference> map = new HashMap<Integer, Reference>();

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            map.put(i, new WeakReference(new ReferenceObject(i)));
        }

        int i = 0;
        for (Reference r : map.values()) {
            if (r.get() == null) {
                i++;
            }
        }
        System.out.println("被回收的對象數:" + i);
    }

    static class ReferenceObject {
        private int    i;

        private byte[] b;

        public ReferenceObject(int i) {
            this.i = i;
            b = new byte[1024 *10];
        }
    }
}

這里創建大約1000個 10K的 Weak Reference 對象,最后打印的結果是:被回收的對象數:767,這里ReferenceObject如果設置為1K的話,最后的打印結果是0

這個例子並不嚴謹,但是卻說明了被Weak Reference的對象在一定的時候會被jvm回收,但是強引用就不會出現這種狀態。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM