java 垃圾回收總結(可達性分析 引用分類


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 方式創建的對象,並且顯示關聯的對象

1
Object obj = new  Object();

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

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

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

 

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

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

Weak Reference :用來描述非必需對象。即使在root算法中 其引用的對象root可達到,只能生存到下一次垃圾回收之前。

Phantom Reference :無法通過虛引用獲得一個對象的實例,設置虛引用的目的就是能在這個對象被收集器回收時收到一個系統通知。

 

下面可以看一個測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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