目的: 使用垃圾回收器的唯一原因就是:回收程式不再使用的記憶體。 針對的目標對象: Java的垃圾回收器會自動回收不再使用的Java對象,釋放記憶體。但是回收的是用new創建的,分配在堆上的記憶體。 finalize(): 那麼,如果不是用這種方式創建的對象,該怎麼回收?比如:Java調用了本地的c語言方 ...
目的:
使用垃圾回收器的唯一原因就是:回收程式不再使用的記憶體。
針對的目標對象:
Java的垃圾回收器會自動回收不再使用的Java對象,釋放記憶體。但是回收的是用new創建的,分配在堆上的記憶體。
finalize():
那麼,如果不是用這種方式創建的對象,該怎麼回收?比如:Java調用了本地的c語言方法創建了個對象,那麼這時,該對象不是放在堆上的。除非你手動去調用c的free()方法,否則,這個對象將永遠不會被清理。
Java的finalize()方法可以解決上面的問題。垃圾回收器在回收垃圾對象時,會首先去調用該對象的finalize()方法。所以,你可以在finalize()方法中調用c的free()方法。
一般教科書會寫,finalize()用於垃圾回收之前的清理工作,而實際上,除了上面講的極少數情況之外,我們一般情況下並不需要使用finalize()。
不保證發生:
雖然Java的垃圾回收器會根據對象的使用情況自動清理記憶體,但並不一定會發生,如果記憶體還夠用的話,虛擬機一般是不會浪費時間去作清理工作的。
如何判斷Java對象可以回收:
1.不被使用的“引用計數器法”:
每個對象都含有一個引用計數器,當有引用變數指向該對象時,引用計數器+1,當這個引用變數不再指向該對象,或者被置為null時,計數器-1。如下圖:
當第四種情況發生時,即:沒有引用變數指向“李四”那個對象了,這時,垃圾回收器在恰當的時候就會把李四所在的對象回收掉。
它簡單便捷,但是之所以沒被Java虛擬機採用的原因是:無法解決迴圈引用的問題。舉個簡單的例子:
objA有個instance變數,objB也有個instance變數,讓objA的instance指向B對象,而讓objB的instance變數指向A對象,那麼,B對象和A對象的引用計數器都是1,不為0,如果按照引用計數器的方法,A和B就不能被回收,但事實是,objA和objB這兩個引用變數已經是null了(它們指向的具體對象已經不再被引用了)。
2.根搜索演算法
在主流的商用程式語言中(Java和C#,甚至古老的人Lisp語言),都是使用根搜索演算法(GC Roots Tracing)判定對象是否存活的。
之前講過,對象的引用是放在棧中的,常量的引用是放在常量池之中的。如圖:
根搜索演算法的思想是,從常量池和棧中的引用變數開始遍歷所有的引用變數,找到所有的活的對象(引用不為null)。然後再繼續尋找這個對象所包含的所有引用,反覆進行,直到所有引用網路被訪問完為止。
常量池或棧中的引用變數是根節點,擴展出的整個網路就是一個引用鏈。最後,如果最終發現有對象到根節點的路徑是不可達的,說明這個對象是可回收的,這就解決了迴圈引用的問題:
如上圖,GCRoots是根節點,object5、6、7雖然各自引用,但是它們到GCRoots都是不可達的,所以,它們是可以被回收的。
怎樣回收?
每個虛擬機採用的回收演算法是不同的,經典的案例如下:
標記-清除演算法:
在使用“根搜索演算法”尋找引用變數的同時,虛擬機會給每個存活的對象做一個標記,全部標記完成的時候才進行清除工作。
這樣的問題是,存活的對象在堆中不是連續存儲的,那麼清除“死亡”對象後,記憶體中就會留下大量碎片,如果在後面需要用到大記憶體對象時,記憶體空間不夠,就要重新整理記憶體。如圖回收前:
回收後:
複製演算法:
它將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的記憶體用完了,就將還存活著的對象複製到另外一塊上面,然後再把已使用過的記憶體空間一次清理掉。如圖回收前:
回收後(把存活著的對象搬到右側,左側剩下的就都是可清理的,然後統統清理掉。當右側需要清理的時候,類似的,把存活的對象再搬到左側,然後清空右側):
這種方式的缺點:很顯然,可用記憶體只有原來的一半兒。還有個缺點:如果左側大量的都是存活的對象,清理時仍然要全部搬到右側,很浪費時間。
現在的商業虛擬機都採用這種收集演算法,但是保留區與運作區的比例有不同,且詳細又將堆記憶體劃分為新生代、老年代。新生代 ( Young ) 又被劃分為三個區域:Eden、From Survivor、To Survivor。關於新生代、老年代、堆記憶體等,詳細可查閱關於Java虛擬機的資料瞭解。
參考資料:
1.Thinking in Java 第5.5.4章節。
2.cnblogs:Java垃圾收集器:
http://www.cnblogs.com/gw811/archive/2012/10/19/2730258.html
3.blogjava:Java堆記憶體:
http://www.blogjava.net/fancydeepin/archive/2013/09/29/jvm_heep.html
更多內容請關註微信訂閱號:it_pupil