1、引用計數法
如果某個地方引用了這個對象就+1,如果失效了就-1,當為0就會回收但是JVM沒有用這種方式,因為無法判定相互循環引用(A引用B,B引用A)的情況
所謂引用計數法就是給每一個對象設置一個引用計數器,每當有一個地方引用這個對象時,就將計數器加一,引用失效時,計數器就減一。當一個對象的引用計數器為零時,說明此對象沒有被引用,也就是“死對象”,將會被垃圾回收。
引用計數法有一個缺陷就是無法解決循環引用問題,也就是說當對象A引用對象B,對象B又引用者對象A,那么此時A,B對象的引用計數器都不為零,也就造成無法完成垃圾回收,所以主流的虛擬機都沒有采用這種算法。
2、引用鏈法(可達性分析)
通過一種GC ROOT的對象(虛擬機棧(棧幀中的本地變量表)中引用的對象、方法區中類靜態屬性引用的對象、方法區中常量引用的對象、本地方法棧中JNI(即一般說的Native方法)引用的對象)來判斷,如果有一條鏈能夠到達GC ROOT就說明,對象還在被引用,不能到達GC ROOT就說明對象已經不再被引用,可以回收
可達:
不可達:
- 雖然這些算法可以判定一個對象是否能被回收,但是當滿足上述條件時,一個對象不一定會被回收。當一個對象不可達GCRoot時,這個對象並不會立馬被回收,而是處於一個死緩的階段,若要被真正的回收需要經歷兩次標記如果對象在可達性分析中沒有與GCRoot的引用鏈,那么此時就會被第一次標記並且進行一次篩選,篩選的條件是是否有必要執行finalize()方法。當對象沒有覆蓋finalize0方法或者已被虛擬機調用過,那么就認為是沒必要的。如果該對象有必要執行finalize0方法,那么這個對象將會放在一個稱為F-Queue的對隊列中,虛擬機會觸發一個Finalize0線程去執行,此線程是低優先級的,並且虛擬機不會承諾一直等待它運行完,這是因為如果finalize()執行緩慢或者發生了死鎖,那么就會造成F-Queue隊列一直等待,造成了內存回收系統的崩潰。執行方法完畢后,GC會再次判斷該對象是否可達,若不可達,則進行回收,否則,對象“復活”。
- finalize0方法是Object提供的的實例方法,子類可以覆蓋這個方法來做一些系統資源的釋放或者數據的清理可以在finalize()讓這個對象再次被引用,避免被GC回收;但是最常用的目的還是做cleanup,Java不保證這個finalize()一定被執行。有⼀種JNI(Java Native Interface)調⽤用non-Java程序(C或C++), finalize()的⼯工作就是回收這部分的內存。
- 創建對象的時候調用構造函數,銷毀對象的時候調用析構(如:finalize)函數,即一個對象變為垃圾的時候會自動調用finalize函數
在Student類里面重寫finalize方法:
public String sname; public int age; protected void finalize(){ System.out.println("finalize方法執行了"); }
測試:
public static void main(String[] args) { Student student=new Student("zhai",12); student=new Student("zhang",13); System.gc();//強制回收垃圾,不調用gc函數,finalize方法不一定執行 }
3、java的垃圾回收機制
在java中,程序員是不需要顯式地去釋放一個對象的內存的,而是由虛擬機自行執行。在JVM中,有一個垃圾回收線程,它是低優先級的,在正常情況下是不會執行的,只有在虛擬機空閑或者當前堆內存不足時,才會觸發執行,掃描那些沒有被任何引用的對象,並將它們添加到要回收的集合中,進行回收。