討論篇:靜態變數生命週期到底何如?如何使用?

散人丶發表於2019-02-21

靜態變數的生命週期

​ 最近有個朋友問我個問題,為什麼用靜態變數作為一個標誌位儲存,但是時常資料丟失,有時候又是可行的,原因究竟為何。

public static boolean flag = false
複製程式碼

​ 熟悉Java記憶體模型的朋友都知道,靜態變數是儲存在方法區之中的,而靜態變數又屬於整個類的例項,那麼靜態變數整個的生命週期是什麼樣子,什麼時候會被回收呢?在此之前其實沒有太多考慮這個問題,工作涉及到靜態變數的地方一般也就是單例模式,但也沒有出現過被回收的情況,加上工作以來,很多前輩同事也教導過不要使用太多的靜態變數,所以,對這個問題自己也一直是一知半解,沒有認真思考這個問題,在此參考了一些資料,做一些記錄。

1.類的生命週期

上述也說道,靜態變數是屬於類的例項物件,所以靜態變數的生命週期實際也就是類的生命週期。

當我們編寫一個Java的原始檔後,經過編譯會生成一個字尾名為class的檔案,這種檔案叫做位元組碼檔案,只有這種位元組碼檔案才能夠在Java虛擬機器中執行,Java類的生命週期就是指一個class檔案從載入到解除安裝的全過程。

一個Java類的完整的生命週期會經歷載入、連線、初始化、使用、和解除安裝五個階段,這裡就不都進行討論了,我們主要看載入和解除安裝兩個階段

1.1 載入

對於載入的時機,各個虛擬機器的做法並不一樣,但是有一個原則,就是當jvm“預期”到一個類將要被使用時就會在使用它之前對這 個類進行載入。比如說,在一段程式碼中出現了一個類的名字,jvm在執行這段程式碼之前並不能確定這個類是否會被使用到,於是,有些jvm會在執行前就載入這個類,而有些則在真正需要用的時候才會去載入它,這取決於具體的jvm實現。我們常用的hotspot虛擬機器是採用的後者,就是說當真正用到一個類的時候才對它進行載入。

載入階段是類的生命週期中的第一個階段,載入階段之後,是連線階段。有一點需要注意,就是有時連線階段並不會等載入階段完 全完成之後才開始,而是交叉進行,可能一個類只載入了一部分之後,連線階段就已經開始了。但是這兩個階段總的開始時間和完成時間總是固定的:載入階段總是在連線階段之前開始,連線階段總是在載入階段完成之後完成.

也就是類似懶載入的思想,在將要使用到的時候去載入這個類,這樣也能避免浪費記憶體資源,當然,具體的實現就取決於具體的Jvm了

1.2 解除安裝

jvm解除安裝類的判定條件如下:參照hotspot虛擬機器的一句話

1.該類所有的例項都已經被回收,也就是java堆中不存在該類的任何例項。
2.載入該類的ClassLoader已經被回收。
3.該類對應的java.lang.Class物件沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。
複製程式碼

從上面的條件可以看到,一個類被解除安裝的條件還是挺苛刻的,首先所有類的物件例項要被回收,二者,載入該類的ClassLoader要被回收,這個條件就基本等同於程式掛掉,最後是Class物件無任何引用。

從上面結論基本可以判斷,靜態變數的生命週期基本是從這個類載入,到整個程式死掉的過程,注意退出APK不代表殺死程式,這時候,靜態變數還是有效的,程式重啟後靜態變數重新生成。依據根搜尋演算法,物件是否會被垃圾收集與未被使用時間長短無關,僅僅在於這個物件是不是“活”的。

同時我們也應該注意到,靜態變數生命週期的如此之長,導致靜態變數基本很難被GC回收掉,是非常佔用記憶體的,所以在開發過程中,儘量少使用一些靜態屬性的變數,減少記憶體佔用。

以上也是僅代表個人意見,期待各位朋友的發言

相關文章