局部變量表
局部變量表(Local Variable Table)是一組變量存儲空間,用於存放方法參數和方法內部定義的局部變量。局部變量表的容量一變量槽(Variable Slot,下稱Slot)為最小單位。
為了儘可能節省棧幀空間,局部變量表中的Slot是可以重用的,方法體中定義的變量其作用於域不一定會覆蓋整個方法體,如果當前字節碼PC計數器的值已經超出了某個變量的作用域,那這個變量對應的Slot就可以交給其他變量使用。
Slot的複用會直接影響到系統的垃圾收集行為。
局部變量表Slot複用對垃圾收集的影響之一:
代碼很簡單,向內存填充了64MB數據,然後通知虛擬機進行垃圾回收。
我們在虛擬機運行參數中加"-verbose:gc"來查看垃圾收集的過程,
發現System.gc()運行後並沒有收回這64MB內存。
下面是運行結果:
沒有回收placeholder所佔的內存能說得過去,因為在執行System.gc()時,變量placeholder還出於作用域之內,虛擬機自然不敢回收placeholder的內存。
那麼我們對代碼再次進行修改:
加上花括號以後,placeholder作用於就限定在花括號以內,執行System.gc()的時候,placeholder已經不可能再被訪問,我們看一下執行結果:
我們發現還是有64MB的內存沒有被回收。
我們再次修改代碼:
在System.gc()前加入int a=0;再運行一次程序查看一下結果:
發現這次內存真的被回收了。
placeholder是否被回收的根本原因是:局部變量中的Slot是否還存在關於placehoder的數組對象的引用。
第一次修改中,代碼雖然已經離開了placeholder的作用於,但是此後沒有任何對局部變量表的讀寫操作,placeholder原本佔用的Slot還沒有被其他變量所複用,所以作為GC Roots一部分的局部變量表仍保留著對它的關聯。這種關聯沒有及時打斷,在絕大部分情況下影響都很輕微。
但是遇到一個不再使用的變量,手動將其設置為null(用來代替 int a=0,吧變量對應的局部變量表Slot清空)不見得是一個無意義的操作,這種操作可以作為一種在極其特殊情況下的“奇技”來使用。
運行結果還是一樣:
《Practical Java》中把“不使用的對象應手動複製為null”作為一個推薦的編碼原則。
通過本例可以發現,這種操作在某種情況下確實很有用。
本書作者認為並不應該對複製null過於依賴,因為:
以恰當的變量作用域來控制變量回收時間才是最優雅的解決方案。
賦值null的操作經過JIT編譯優化以後就會被消除,赭石將變量設置為null就沒有意義。字節碼被便以為本地代碼以後,對GC Roots的枚舉額於執行使其有巨大區別。在經過JIT編譯後,第一次修改後的代碼就可以正確回收掉內存。
局部變量不會類變量存在“準備階段”
類變量有兩次賦值初始值的過程,一次在準備階段,賦予系統初始值;另外一個在初始化階段,賦予程序員定義的初始值。即在初始化階段程序員沒有為類變量賦值也沒有關係,類變量仍然具有一個默認的初始值。
可以正確執行,輸出為:0。
但是局部變量不一樣,如果局部變量定義了但是沒有賦初值是不能使用的,不要認為Java中任何情況下都存在諸如整型變量默認值為0,布爾類型默認值為false等。
如下代碼:
不能運行,編譯期間會檢查到這一點。
閱讀更多 明明如月學長 的文章