局部變量表影響GC之實例解析

最近GC相關的資料,看到一些好玩的東西,在些總結一下!


代碼清單1-1

public static void main(String[] args){
		
      byte[] placeholder = new byte[64*1024*1024];
      System.gc();
}
代碼清單1-1中的代碼很簡單,即向內存填充了64MB的數據,然後通知虛擬機進行垃圾收集。我們在虛擬機運行參數中加上“-verbose:gc” 來看看垃圾收集的過程,發現在System.gc()運行後並沒有回收這64MB的內存,下面是運行的結果:

[GC 68157K->66120K(249344K), 0.0009462 secs]
[Full GC 66120K->66000K(249344K), 0.0088075 secs]

說明:沒有回收placeholder所佔的內存能說得過去,因爲在執行System.gc()時,變量placeholder 還處於作用域內,虛擬機自然不敢回收placeholder的內存。
把代碼修改一下,變成代碼清單1-2


代碼清單1-2
public static void main(String[] args){
		
	byte[] placeholder = new byte[64*1024*1024];
		
	placeholder = null;
		
	System.gc();
}
代碼清單1-2中,手動把大對象placeholder置爲null,運行結果爲:
[GC 68157K->66072K(249344K), 0.0032981 secs]
[Full GC 66072K->464K(249344K), 0.0163352 secs]

說明:從運行結果可以看出來,placeholder對象的內存被回收掉了!


把代碼修改一下,變成代碼清單1-3


代碼清單1-3
public static void main(String[] args){
    {
        byte[] placeholder = new byte[64*1024*1024];
    }
    System.gc();
}
加了花括號之後,placeholder的作用域被限定在花括號之內,從代碼邏輯上講,在執行System.gc()的時候,placeholder已經不可能再被訪問了,但執行一下這段程序 ,會發現運行結果如下,還是64MB的內存沒有被回收,這又是爲什麼呢?
[GC 68157K->66104K(249344K), 0.0009837 secs]
[Full GC 66104K->66000K(249344K), 0.0078921 secs]

在解析爲什麼之前,我們先對這段代碼再進行修改,在調用System.gc()之前加入一行“int a=0;”,變成代碼清單1-4的樣子:

代碼清單1-4
public static void main(String[] args){
    {
        byte[] placeholder = new byte[64*1024*1024];
    }
    int a = 0;
    System.gc();
}


這個修改看起來很莫名奇妙,但運行一下程序,卻發現這次內存真的被回收了。
[GC 68157K->66104K(249344K), 0.0008698 secs]
[Full GC 66104K->464K(249344K), 0.0087568 secs]

在代碼清單1-1中,代碼雖然已經離開了placeholder的作用域,但在此之後,沒有任何對局部變量表的讀寫操作placeholder原本所佔用的Slot還沒有被其他變量所複用,所以GC Root一部分的局部變量表仍然保持着對它的關聯。這種關聯沒有被及時打斷,在絕大部分情況下影響都很輕微。但如果遇到一個方法,其後面的代碼有一些耗時很長的操作,而前面又定義了佔用了大量的內存、實際上已經不再使用的變量,那問題就被放大,解決方法就是如代碼清單1-2手動將其設置爲null值或如代碼清單1-4用int a=0把變量對應的局部變量表Slot清空。


代碼清單1-5
public static void main(String[] args){
		
      foo();
		
      System.gc();
		
}
	
public static void foo(){
     byte[] placeholder = new byte[64*1024*1024];
}

代碼清單1-5的運行結果:
[GC 68157K->66120K(249344K), 0.0011992 secs]
[Full GC 66120K->464K(249344K), 0.0084784 secs]


可以看到64M的內存已經被回收,placeholder是foo方法的局部變量,在main方法中調用的時候,其實foo方法對應的棧幀已經結束.那麼placeholder指向的大對象自然被gc的時候回收了.




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章