jvm是如何判斷對象可以被回收的?
方法一:引用計數法
jvm給對象中添加一個引用計數器,每當有一個地方引用它,計數器加1,當引用失效,計數器減1,任何時候計數器為0的對象就是不可能再被使用的。
此方法存在的局限性:不能解決對象之間相互引用的問題。例如:
如上圖所示,棧中引用了堆中對象objA和objB,此時objA和objB的引用計數器都加1;而且把objA賦值給objB的成員變量,objB賦值給objA的成員變量,也就是說objA和objB之間存在引用,此時objA和objB的引用計數器都再加1變成2。而當棧中對objA和objB的引用失效了之后,此時objA和objB實際上已經是無用對象了,但是objA和objB的引用計數器也都只是減1尚且不為0,因此objA和objB兩個對象都不會被回收。任然占用堆的內存。這樣的情況多了就可能出現內存泄漏問題。
方法二:可達性分析算法
通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。
在Java語言中,可作為GC Roots的對象包括下面幾種:
虛擬機棧(棧幀中的本地變量表)中引用的對象。
方法區中類靜態屬性引用的對象。
方法區中常量引用的對象。
本地方法棧中JNI(即一般說的Native方法)引用的對象
方法三:
常見引用類型:
強引用:普通的變量引用
軟引用:將對象用SoftReference軟引用類型的對象包裹,正常情況不會被回收,但是GC做完后發現釋不出空間存放新的對象,則會把這些軟引用的對象回收掉。軟引用可用來實現對內存敏感度不高的高速緩存。public static SoftReference<User> user = new SoftReference<User>(new User());
弱引用:將對象用WeakReference弱引用類型的對象包裹,弱引用跟沒有引用差不多,GC會直接回收掉,很少用。
public static WeakReference<User> user = new WeakReference<User>(new User());
虛引用:虛引用也成為幽靈引用或幻影引用,它是最弱的一種引用,幾乎不用。
方法四:finalize()方法最終判定對象是否存活
即使在可達性分析算法中不可達的對象,也並非是“非死不可”de ,這時候它們暫時處於“緩刑”階段,要真正宣告一個對象死亡,至少要經歷再次標記過程。
標記的前提是對象在進行可達性分析后發現沒有與GC Roots相連接的引用鏈。
1、第一次標記並進行一次篩選:
對象沒有覆蓋finalize()方法,對象將直接被回收。
2、第二次標記:
如果這個對象覆蓋了finalize()方法,只要重新與引用鏈上的任何一個對象建立關聯即可。
PS:如何判斷一個類是無用的類
類需要同時滿足下面3個條件才能算是無用的類:
1、該類所有的實例都已經被回收,也就是堆中不存在該類的任何實例。
2、加載該類的ClassLoader已經被回收。
3、該類對應的java.lang.Class對象沒有任何地方唄引用,無法在任何地方通過反射訪問該類的方法。