一次有趣的局部變量GC

前言

最近在看《實戰Java虛擬機》, 發現書上的一個關於局部變量表GC挺有意思,先上代碼。

主角

沒有GC

public class Main {
    public static void reversion(){
        {
            byte[] a = new byte[6*1024*1024];
        }
        System.gc();
    }

    public static void main(String[] args) {
        reversion();
    }
}

分配了一塊6MB的堆空間,並使用局部變量引用這塊空間, 然後顯式進行一次Full GC。

image-20181029192234982

先配置一下JVM參數用於打印GC log

可以看到這塊6MB的堆空間並沒有被回收, 接下來加一行代碼就能使得堆空間被回收。

可以GC

public class Main {
    public static void reversion(){
        {
            byte[] a = new byte[6*1024*1024];
        }
        int c = 0;
        System.gc();
    }

    public static void main(String[] args) {
        reversion();
    }
}

可以看到這6MB的空間已經被回收了,僅僅因爲多了一句看似與a毫無關係的 int c = 0;

答案

藉助jclasslib工具我們進一步查看函數的局部變量信息, 在此之前我們需要對代碼做一點小改動再進行分析

public class Main {
    public static void reversion(){
        {
            byte[] a = new byte[6*1024*1024];
            System.out.println(a[0]);
        }
        int c = 0;
        System.gc();
    }

    public static void main(String[] args) {
        reversion();
    }
}

tips:JVM即時編譯器擁有死代碼消除的特性,a數組並沒有被任何地方使用,即時編譯器可以精簡數據流,並且減少編譯時間以及最終生成機器碼的大小。簡單的說如果a沒有被使用的話會被編譯成var0, 我們在本地變量表中看不到a了。

可以看到a和c在局部變量表中的索引值都是0,也就是說c重用了a在局部變量表中的槽位,從而使得a指向的堆空間能夠被GC回收

棧幀中的局部變量表中的槽位是可以重用的,如果一個局部變量過了其作用域,那麼在其作用域之後申明的新的局部變量就很有可能複用過期局部變量的槽位,從而達到節省資源的目的

總結

通過這個GC的小例子,切實感受到了JVM對於資源節省的嚴苛程度,對於作用域的細粒度把控之強大。給大家推薦我讀的這本《實戰Java虛擬機》,切實的一本好書,所謂好書無非兩點:1.能讀懂 2.有所獲。同時也推薦極客時間的《深入拆解虛擬機》,多路學習,兼聽則明。

最後打個小廣告~

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