GC,即就是Java垃圾回收機制。目前主流的JVM(HotSpot)采用的是分代收集算法。與C++不同的是,Java采用的是類似於樹形結構的可達性分析法來判斷對象是否還存在引用。即:從gcroot開始,把所有可以搜索得到的對象標記為存活對象。
GC機制
要准確理解Java的垃圾回收機制,就要從:“什么時候”,“對什么東西”,“做了什么”三個方面來具體分析。
第一:“什么時候”即就是GC觸發的條件。GC觸發的條件有兩種。(1)程序調用System.gc時可以觸發;(2)系統自身來決定GC觸發的時機。
系統判斷GC觸發的依據:根據Eden區和From Space區的內存大小來決定。當內存大小不足時,則會啟動GC線程並停止應用線程。
第二:“對什么東西”籠統的認為是Java對象並沒有錯。但是准確來講,GC操作的對象分為:通過可達性分析法無法搜索到的對象和可以搜索到的對象。對於搜索不到的方法進行標記。
第三:“做了什么”最淺顯的理解為釋放對象。但是從GC的底層機制可以看出,對於可以搜索到的對象進行復制操作,對於搜索不到的對象,調用finalize()方法進行釋放。
具體過程:當GC線程啟動時,會通過可達性分析法把Eden區和From Space區的存活對象復制到To Space區,然后把Eden Space和From Space區的對象釋放掉。當GC輪訓掃描To Space區一定次數后,把依然存活的對象復制到老年代,然后釋放To Space區的對象。
對於用可達性分析法搜索不到的對象,GC並不一定會回收該對象。要完全回收一個對象,至少需要經過兩次標記的過程。
第一次標記:對於一個沒有其他引用的對象,篩選該對象是否有必要執行finalize()方法,如果沒有執行必要,則意味可直接回收。(篩選依據:是否復寫或執行過finalize()方法;因為finalize方法只能被執行一次)。
第二次標記:如果被篩選判定位有必要執行,則會放入FQueue隊列,並自動創建一個低優先級的finalize線程來執行釋放操作。如果在一個對象釋放前被其他對象引用,則該對象會被移除FQueue隊列。
GC過程中用到的回收算法:
通過上面的GC過程不難看出,Java堆中的年輕代和老年代采用了不同的回收算法。年輕代采用了復制法;而老年代采用了標記-整理法
具體各種回收算法的詳解參考:http://www.cnblogs.com/dolphin0520/p/3783345.html
JVM內存空間圖解
程序計數器:線程私有。是一塊較小的內存,是當前線程所執行的字節碼的行號指示器。是Java虛擬機規范中唯一沒有規定OOM(OutOfMemoryError)的區域。
Java棧:線程私有。生命周期和線程相同。是Java方法執行的內存模型。執行每個方法都會創建一個棧幀,用於存儲局部變量和操作數(對象引用)。局部變量所需要的內存空間大小在編譯期間完成分配。所以棧幀的大小不會改變。存在兩種異常情況:若線程請求深度大於棧的深度,拋StackOverflowError。若棧在動態擴展時無法請求足夠內存,拋OOM。
Java堆:所有線程共享。虛擬機啟動時創建。存放對象實力和數組。所占內存最大。分為新生代(Young區),老年代(Old區)。新生代分Eden區,Servior區。Servior區又分為From space區和To Space區。Eden區和Servior區的內存比為8:1。 當擴展內存大於可用內存,拋OOM。
方法區:所有線程共享。用於存儲已被虛擬機加載的類信息、常量、靜態變量等數據。又稱為非堆(Non – Heap)。方法區又稱“永久代”。GC很少在這個區域進行,但不代表不會回收。這個區域回收目標主要是針對常量池的回收和對類型的卸載。當內存申請大於實際可用內存,拋OOM。
本地方法棧:線程私有。與Java棧類似,但是不是為Java方法(字節碼)服務,而是為本地非Java方法服務。也會拋StackOverflowError和OOM。
Minor GC ,Full GC 觸發條件
Minor GC觸發條件:當Eden區滿時,觸發Minor GC。
Full GC觸發條件:
(1)調用System.gc時,系統建議執行Full GC,但是不必然執行
(2)老年代空間不足
(3)方法去空間不足
(4)通過Minor GC后進入老年代的平均大小大於老年代的可用內存
(5)由Eden區、From Space區向To Space區復制時,對象大小大於To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小於該對象大小
