本文章內容基于《深入了解Java虛擬機》第二版嫉嘀,對里面知識點進行總結砾莱,JDK主要是1.6和1.7槽片。
1. 確定對象可以被回收
1.1 引用計數算法
每個對象擁有一個計算器值上岗,每當一個地方引用他則計數器值+1福荸;引用失效則-1.
當計數器值為0,則說明對象可以被回收
缺點:很難解決對象之間互相虛幻引用的問題肴掷,目前虛擬機很少使用該算法敬锐。
1.2 可達性分析算法
通過一系列“GC Roots”的對象作為起始點,從這些節(jié)點向下搜索呆瞻,經過路徑為“引用鏈”台夺,如果搜索到則可達,否則不可達痴脾。
GC Roots對象包括:
1颤介、虛擬機棧(棧幀中的本地變量表)中引用的對象。
2明郭、方法區(qū)中類靜態(tài)屬性引用的對象
3买窟、方法區(qū)中常量引用的對象
4丰泊、本地方法棧中JNI(一般為Native方法)引用的對象
1.3 引用四種類型
1薯定、強引用:普遍存在,例如“Object obj = new Object()”瞳购,只要引用存在话侄,永遠不會被回收
2、軟引用:當系統(tǒng)要發(fā)現內存溢出異常之前,對這部分對象列入回收范圍進行二次回收年堆。SoftReference類實現
3吞杭、弱引用:垃圾收集器工作時,這部分對象都會被回收变丧。WeakReference類實現
4芽狗、虛引用:無法通過虛引用獲取一個對象的實例,唯一目的就是該對象被收集器回收時收到一個系統(tǒng)通知痒蓬。PhantomReference類實現
1.4 finalize()函數
每個對象被標記成不可達童擎,會經歷兩次標記過程。如果對象沒有覆蓋finalize()函數或已經被執(zhí)行故一次攻晒,就判斷對象已經死亡顾复。
其實執(zhí)行finalize()函數可以將不可達本身對象變成可達,讓對象依然可以存活鲁捏。(但是建議不使用finalize()函數)
1.5 回收方法區(qū)(永生代)
這部分區(qū)域回收內容:廢棄常量和無用的類
1芯砸、廢棄的常量:例如常量池中字符串,跟Java堆的對象一樣给梅;常量池的字符串沒有String對象引用假丧,就會被收回。
2破喻、無用的類:需要同時滿足以下條件
a虎谢、該類所有的實例都被回收。(Java堆不存在該類的任何實例)
b曹质、加載該類的ClassLoader已經被回收婴噩。
c、該類對應的java.lang.Class對象沒有在任何地方被引用羽德,無法在任何地方通過反射訪問該類的方法几莽。
及時滿足以上條件,虛擬機也非必然回收宅静。虛擬機都會提供參數來控制是否對類進行回收章蚣。
注意:在大量使用反射、動態(tài)代理姨夹、CGLib等ByteCode框架纤垂、動態(tài)生成JSP以及OSGi這類頻繁自定義ClassLoader的場景需要虛擬機具備類協(xié)助的功能,保證方法區(qū)不會溢出磷账。
2. 垃圾收集算法
2.1 標記-清除算法
先把標記出所有需要回收的對象峭沦,標記之后統(tǒng)一回收所有被標記的對象。
缺點:1逃糟、效率不高 2吼鱼、消除之后生產大量不連續(xù)的內存碎片
2.2 復制算法
復制算法為了解決效率問題和碎片問題
將內存分為兩個相同大小蓬豁,每次只使用其中一塊。當這一塊內存使用完菇肃,則將還存活的對象復制到另外一個塊地粪,然后對使用過內存空一次性清理掉。
缺點:內存要一分為二琐谤,內存浪費比較高
目前商用虛擬機都采用該算法回收新生代蟆技。
其他知識點:IBM公司研究新生代98%都是很快消亡,不需要1:1劃分內存斗忌。
只需要一塊較大Eden空間和兩塊較小的Survivor空間付魔。每次使用Eden和其中一塊Survivor空間》甚澹回收時候將存活對象復制到另外一塊Survivor空間几苍。目前HostSpot虛擬機默認Eden和Survivor比例為8:1。
注意:無法保證每次回收都不多于10%的對象存活陈哑,當Survivor空間不夠時使用其他內存進行分配擔保(比如老年代)妻坝。
2.3 標記-整理算法
復制算法在對象存活率較高的情況之下,由于執(zhí)行過多的復制操作惊窖,效率將變低刽宪。內存空間浪費較多,老生代一般不使用該算法界酒。
針對老生代圣拄,提出標記-整理算法,將所有存活的對象都向一端移動毁欣,然后直接清除端邊界以外的內存庇谆。
2.4 分代收集算法
目前主要為新生代和老年代,針對這兩塊的空間對應的收集算法是不同的凭疮。
3. HotSpot的算法實現
3.1 枚舉根節(jié)點
可達性分析問題:
1饭耳、如果從全局性的引用和執(zhí)行上下問(棧幀的本地變量表)中區(qū)搜索對象是否可達會消耗很多時間。
2执解、可達性分析為了保持“一致性”寞肖,會停止系統(tǒng)所有執(zhí)行線程。(主要還是優(yōu)化時間長度衰腌,無法完全解決)
目前所有虛擬機采用準確式GC
HotSpot使用一組OopMap的數據結構來實現虛擬機直接知道對象引用存放的地方新蟆。(不需要檢查所有上下文和全局引用)(記錄對象內各種類型數據的偏移量)
JIT編譯過程,會在特定位置記錄下棧和寄存器哪些位置是引用右蕊。
3.2 安全點
引用關系變化或OopMap內容變化的指令很多琼稻,如果為每一條指令都生成對應的OopMap,則需要大量的額外空間尤泽。
HotSpot實現并非為每一條命令都創(chuàng)建OopMap欣簇。
上一節(jié)所說的特定位置指的就是安全點。即程序執(zhí)行時并非所有地方都可以停頓坯约,只有在到達安全點才能暫停熊咽,然后在開始GC。
問題:如果讓所有線程在開始GC之后都執(zhí)行到安全點上停頓下來闹丐。
方案1:搶先式中斷-先讓所有線程中斷横殴,然后恢復讓不在安全點線程繼續(xù)執(zhí)行到安全點。(目前已經沒有這種實現方式)
方案2:主動式中斷-設置一個標志卿拴,各線程執(zhí)行時主動區(qū)輪詢該標志衫仑。發(fā)現中斷標志就中斷刮起(中斷標志和安全點是重合、還有創(chuàng)建對象需要分配內存的地方)
3.1 安全區(qū)域
如果程序沒有分配CPU時間堕花,就無法執(zhí)行到安全點然后掛起文狱。(部分Sleep狀態(tài)或Blocked狀態(tài))通過安全區(qū)域來解決該問題。
安全區(qū)域表示該段代碼中缘挽,引用關系不會發(fā)生變化瞄崇。
JVM執(zhí)行GC時,不用管執(zhí)行到安全區(qū)域的線程(狀態(tài)判斷)壕曼。線程要離開安全區(qū)需要判斷系統(tǒng)是否完成根節(jié)點枚舉(或整個GC)