##JAVA 垃圾回收機制(一) — 對象回收與算法初識
一、概念
這裏說的GC回收,指的是 Java 堆的地方, 深入Java虛擬機之 — JVM的愛恨情仇 文章 中,我們知道了程序計算器,虛擬機棧和本地方法棧都是隨線程開啓,隨線程關閉的,因此這幾塊區域的內存分配和回收都具備確定性。而Java 堆和方法區則不一樣,一個接口中的多個實現類需要的內存可能不一樣,一個方法中的多個分支需要的內存也可能不一樣,只有程序在運行時,才知道創建了哪些對象,這部分內存的分配和回收都是動態的,垃圾收集器所關注的就是這部分內存。
而 GC 關注的也就3個點:
- 哪些對象需要回收
- 什麼時候回收
- 如何回收
二、哪些對象需要回收
怎麼判斷對象是“存活” 的,還是已經"死亡"呢?主要有以下方法:
- 1.引用計算算法: 給對象添加一個引用計算器,每當有一個地方引用它,則加1,當引用失效,則減1;任何時刻計算器爲0的對象就是不可能再被使用的。但它很難解決對象之間相互循環引用的問題,所以主流的Java虛擬機都沒有采用這種算法。
- 2.可達性分析算法:
通過一系列的 “GC Roots” 的對象作爲起始點,從這些起始點開始向下搜索,搜索走過的路徑被稱爲引用鏈(Reference Chain),當一個對象到GC Roots 沒有任何引用鏈項鍊,即GC Roots 不可達,則證明此對象是不可用的。
三、什麼時候回收
- 1.在可達性分析算法不可達的對象,也不一定"非死不可’,它會經歷兩次標記,一是當不可達 GC Roots 時,標記一次並篩選,篩選的條件是該對象是否有必要執行 finalize 方法。當對象沒有覆蓋 finalize 方法,或者已經執行過 finalize 方法時,則認爲此對象會被回收。
- 2.上面都是對 Java 堆進行回收,雖說 Java 堆 可以回收70%~95%的空間,但方法區同樣可以回收一些資源,方法區主要回收兩個部分廢棄常量和無用的類。
四、如何回收
-
4.1 標記-清除算法
這個算法首先標記處所有需要回收的對象,在標記完成之後統一回收所有被標記的對象。
看似美好,實則不然,主要有以下兩個缺點:
效率問題:標記和清除兩個效率都不高
空間問題:標記清除後,會產生大量空間碎片,在大對象需要分配空間時,找不到內存,從而又觸發 GC 操作。 -
4.2 複製算法
基於 標記-清除算法的效率問題,複製算法出現的。
這種算法是把內存分爲相等的兩塊,一塊用來存儲對象,當GC操作後,把還存留的對象移動到未存對象的那塊內存區域,再把使用過的內存清掉。這樣每次都對整個半區進行內存回收,就不用擔心內存碎片的問題了。 -
4.3 標記-整理算法
標記整理算法,是針對老年代的。它與標記-清除算法一樣,但不是對相對進行清除,而是移動到另一端,分兩個步驟
1.標記那些被引用的對象
2.將被標記的對象移動按順序移動到一端,然後清除掉可回收的對象 -
4.4、分代收集算法
在商業的虛擬機中,都採用 分代收集算法,根據對象存活週期將內存劃分爲幾塊,即新生代和老年代;
在新生代中,每次都有大量對象死去,只有少量活着,就選用複製算法;而老年代的對象存活率比較高,沒有額外空間對它進行分配擔保,就必須使用 “標記-整理”或者“標記-清理” 進行回收。