Java虛擬機局部變量表Slot複用的影響

Java虛擬機局部變量表Slot複用的影響

局部變量表

局部變量表(Local Variable Table)是一組變量存儲空間,用於存放方法參數和方法內部定義的局部變量。局部變量表的容量一變量槽(Variable Slot,下稱Slot)為最小單位。

為了儘可能節省棧幀空間,局部變量表中的Slot是可以重用的,方法體中定義的變量其作用於域不一定會覆蓋整個方法體,如果當前字節碼PC計數器的值已經超出了某個變量的作用域,那這個變量對應的Slot就可以交給其他變量使用。

Slot的複用會直接影響到系統的垃圾收集行為。

局部變量表Slot複用對垃圾收集的影響之一:

Java虛擬機局部變量表Slot複用的影響

代碼很簡單,向內存填充了64MB數據,然後通知虛擬機進行垃圾回收。

我們在虛擬機運行參數中加"-verbose:gc"來查看垃圾收集的過程,

Java虛擬機局部變量表Slot複用的影響

發現System.gc()運行後並沒有收回這64MB內存。

下面是運行結果:

Java虛擬機局部變量表Slot複用的影響

沒有回收placeholder所佔的內存能說得過去,因為在執行System.gc()時,變量placeholder還出於作用域之內,虛擬機自然不敢回收placeholder的內存。

那麼我們對代碼再次進行修改:

Java虛擬機局部變量表Slot複用的影響

加上花括號以後,placeholder作用於就限定在花括號以內,執行System.gc()的時候,placeholder已經不可能再被訪問,我們看一下執行結果:

Java虛擬機局部變量表Slot複用的影響

我們發現還是有64MB的內存沒有被回收。

我們再次修改代碼:

Java虛擬機局部變量表Slot複用的影響

在System.gc()前加入int a=0;再運行一次程序查看一下結果:

Java虛擬機局部變量表Slot複用的影響

發現這次內存真的被回收了。

placeholder是否被回收的根本原因是:局部變量中的Slot是否還存在關於placehoder的數組對象的引用。

第一次修改中,代碼雖然已經離開了placeholder的作用於,但是此後沒有任何對局部變量表的讀寫操作,placeholder原本佔用的Slot還沒有被其他變量所複用,所以作為GC Roots一部分的局部變量表仍保留著對它的關聯。這種關聯沒有及時打斷,在絕大部分情況下影響都很輕微。

但是遇到一個不再使用的變量,手動將其設置為null(用來代替 int a=0,吧變量對應的局部變量表Slot清空)不見得是一個無意義的操作,這種操作可以作為一種在極其特殊情況下的“奇技”來使用。

Java虛擬機局部變量表Slot複用的影響

運行結果還是一樣:

Java虛擬機局部變量表Slot複用的影響

《Practical Java》中把“不使用的對象應手動複製為null”作為一個推薦的編碼原則。

通過本例可以發現,這種操作在某種情況下確實很有用。

本書作者認為並不應該對複製null過於依賴,因為:

  1. 以恰當的變量作用域來控制變量回收時間才是最優雅的解決方案。

  2. 賦值null的操作經過JIT編譯優化以後就會被消除,赭石將變量設置為null就沒有意義。字節碼被便以為本地代碼以後,對GC Roots的枚舉額於執行使其有巨大區別。在經過JIT編譯後,第一次修改後的代碼就可以正確回收掉內存。

局部變量不會類變量存在“準備階段”

類變量有兩次賦值初始值的過程,一次在準備階段,賦予系統初始值;另外一個在初始化階段,賦予程序員定義的初始值。即在初始化階段程序員沒有為類變量賦值也沒有關係,類變量仍然具有一個默認的初始值。

Java虛擬機局部變量表Slot複用的影響

可以正確執行,輸出為:0。

但是局部變量不一樣,如果局部變量定義了但是沒有賦初值是不能使用的,不要認為Java中任何情況下都存在諸如整型變量默認值為0,布爾類型默認值為false等。

如下代碼:

Java虛擬機局部變量表Slot複用的影響

不能運行,編譯期間會檢查到這一點。

Java虛擬機局部變量表Slot複用的影響


分享到:


相關文章: