在這篇文章中《Jvm運行時數據區》介紹了Java內存運行時區域的各個部分,其中程序計數器、虛擬機棧、本地方法棧,3個區域隨着線程的生存而生存的。內存分配和回收都是肯定的。隨着線程的結束內存天然就被回收了,所以不須要考慮垃圾回收的問題。而Java堆和方法區則不同,各線程共享,內存的分配和回收都是動態的。所以垃圾收集器所關注的都是這部份內存。html
接下來咱們就討論Jvm是怎麼回收這部份內存的。在進行回收前垃圾收集器第一件事情就是肯定哪些對象還存活,哪些已經死去。下面介紹兩種基礎的回收算法。java
給對象添加一個引用計數器,每當有一個地方引用它時計數器就+1,當引用失效時計數器就-1,。只要計數器等於0的對象就是不可能再被使用的。算法
此算法在大部分狀況下都是一個不錯的選擇,也有一些著名的應用案例。可是Java虛擬機中是沒有使用的。數據結構
優勢:實現簡單、判斷效率高。post
缺點:很難解決對象之間循環引用的問題。例以下面這個例子url
Object a = new Object(); Object b = new Object(); a=b; b=a; a=b=null; //這樣就致使gc沒法回收他們。
經過一系列的稱爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有使用任何引用鏈時,則說明該對象是不可用的。spa
主流的商用程序語言(Java、C#等)在主流的實現中,都是經過可達性分析來斷定對象是否存活的。線程
經過下圖來清晰的感覺gc root與對象展現的聯繫。所示灰色區域對象是存活的,Object5/6/7均是可回收的對象htm
在Java語言中,可做爲GC Roots 的對象包括下面幾種對象
優勢:更加精確和嚴謹,能夠分析出循環數據結構相互引用的狀況;
缺點:實現比較複雜、須要分析大量數據,消耗大量時間、分析過程須要GC停頓(引用關係不能發生變化),即停頓全部Java執行線程(稱爲"Stop The World",是垃圾回收重點關注的問題)。
在jdk1.2以後,Java對引用的概念進行了擴充,整體分爲4類:強引用、軟引用、弱引用、虛引用,這4中引用強度依次逐漸減弱。
宣告一個對象死亡,至少要經歷兩次標記。
若是對象進行可達性分析算法以後沒發現與GC Roots相連的引用鏈,那它將會第一次標記而且進行一次篩選。
篩選條件:判斷此對象是否有必要執行finalize()方法。
篩選結果:當對象沒有覆蓋finalize()方法、或者finalize()方法已經被JVM執行過,則斷定爲可回收對象。若是對象有必要執行finalize()方法,則被放入F-Queue隊列中。稍後在JVM自動創建、低優先級的Finalizer線程(可能多個線程)中觸發這個方法;
GC對F-Queue隊列中的對象進行二次標記。
若是對象在finalize()方法中從新與引用鏈上的任何一個對象創建了關聯,那麼二次標記時則會將它移出「即將回收」集合。若是此時對象還沒成功逃脫,那麼只能被回收了。
finalize()是Object類的一個方法、一個對象的finalize()方法只會被系統自動調用一次,通過finalize()方法逃脫死亡的對象,第二次不會再調用;
特別說明:並不提倡在程序中調用finalize()來進行自救。建議忘掉Java程序中該方法的存在。由於它執行的時間不肯定,甚至是否被執行也不肯定(Java程序的不正常退出),並且運行代價高昂,沒法保證各個對象的調用順序(甚至有不一樣線程中調用)。
永久代的垃圾收集主要分爲兩部份內容:廢棄常量和無用的類。
回收廢棄常量與Java堆的回收相似。下面舉個栗子說明
假如一個字符串「abc」 已經進入常量池中,但當前系統沒有一個string對象是叫作abc的,也就是說,沒有任何string對象的引用指向常量池中的abc常量,也沒用其餘地方引用這個字面量。若是這是發生內存回收,那麼這個常量abc將會被清理出常量池。常量池中的其餘類(接口)、方法、字段的符號引用也與此相似。
須要同時知足下面3個條件的才能算是無用的類。
虛擬機能夠對同時知足這三個條件的類進行回收,但不是必須進行回收的。是否對類進行回收,HotSpot虛擬機提供了-Xnoclassgc參數進行控制。
----對《深刻理解Java虛擬機》第3章垃圾收集器與內存分配策略 3.2小節總結。接下來總結3.3小結垃圾收集算法。