說到GC,相信大家都不陌生妆艘,對(duì)于Java開發(fā)的同學(xué)來說,之所以能毫無顧忌的申請(qǐng)新的空間幌陕,不用像c/c++那樣malloc/new之后,還需要free/delete搏熄,就是靠它了逗物。
??在正式了解GC之前,我們先想一想翎卓,我們都想了解它什么?可能會(huì)比較好奇一下幾個(gè)方面:
- GC回收都回收那些內(nèi)存坯门?
- GC什么時(shí)候會(huì)回收內(nèi)存?判定可以回收的條件是怎樣的古戴?
- GC是怎樣回收的矩肩?
帶著以上幾個(gè)問題,我們往下走:
一叉袍、回收哪些內(nèi)存?
經(jīng)過之前JVM內(nèi)存結(jié)構(gòu)的講解喳逛,想必大家已經(jīng)了解到了:jvm運(yùn)行時(shí)數(shù)據(jù)區(qū)棵里,大的來說分2部分——線程公有和線程私有姐呐。私有部分:程序計(jì)數(shù)器(PC)曙砂、jvm棧、本地方法棧麦轰,公有部分:方法區(qū)或稱為perm區(qū)砖织、java堆侧纯。私有部分是線程私有的,隨線程生而生眶熬,滅而滅块请,所以這些地方的內(nèi)存區(qū)域,會(huì)隨著方法的結(jié)束或者線程的結(jié)束而被回收墩新,不需要我們關(guān)心;但是公有的部分就不一樣了绵疲,A線程結(jié)束了臣疑,很可能這個(gè)變量,B線程還在使用讯沈,這時(shí)候就不能回收,所以GC的回收內(nèi)存區(qū)域就是java堆和方法區(qū)缺狠。
二、什么時(shí)候回收內(nèi)存蝴乔?
既然叫GC(Garbage Collection)——垃圾收集器驮樊,那就是說GC只是收集垃圾內(nèi)存片酝,何為垃圾內(nèi)存挖腰?和平時(shí)生活一樣,我們不會(huì)再使用的就是垃圾审轮,就需要回收辽俗。所以GC回收也就是不會(huì)再被使用了的對(duì)象實(shí)例。
那么問題來了崖飘,如何能判定對(duì)象是否還使用了?
2.1引用計(jì)數(shù)算法
給對(duì)象添加一個(gè)引用計(jì)數(shù)器吊圾,每當(dāng)有一個(gè)地方引用它時(shí)翰蠢,計(jì)數(shù)器就加1;當(dāng)引用失效的時(shí)候梁沧,計(jì)數(shù)器減1;任何時(shí)候計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的對(duì)象埃碱。
優(yōu)點(diǎn):判定高效酥泞。
缺點(diǎn):無法解決循環(huán)引用的問題,循環(huán)引用的對(duì)象的計(jì)數(shù)器都是1芝囤,所以永遠(yuǎn)無法被回收。
如下例:
public class ReferenceCountingGC {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
private byte[] bigSize = new byte[2*_1MB];
public static void main(String[] args) {
//1.創(chuàng)建2個(gè)實(shí)例
ReferenceCountingGC objcA = new ReferenceCountingGC();
ReferenceCountingGC objcB = new ReferenceCountingGC();
//2.賦值
objcA.instance = objcB;
objcB.instance = objcA;
//3.對(duì)象引用置為空
objcA = null;
objcB = null;
}
}
以上過程的內(nèi)存圖如下:
??上面步驟一和步驟二執(zhí)行之后羡藐,對(duì)應(yīng)的內(nèi)存結(jié)構(gòu)圖悯许,如下圖一所示。其中對(duì)象A和對(duì)象B的引用值都是2瘩扼。
??步驟三:將A和B的引用都置為空,得到的結(jié)構(gòu)圖如下所示集绰。
??我們可以看到線程結(jié)束,但是對(duì)象A和對(duì)象B是不會(huì)被回收的罕袋。
2.2可達(dá)性分析算法
這個(gè)算法的基本思路就是通過一系列的稱謂“GC Roots"的對(duì)象作為起始點(diǎn)碍岔,從這些節(jié)點(diǎn)開始向下搜索,搜索所有走過的路徑為引用鏈蔼啦,當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈項(xiàng)鏈時(shí),則證明此對(duì)象時(shí)不可用的。 如下圖:
可作為GC Roots的對(duì)象包括下面幾種:
- 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
- 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
- 方法區(qū)中常量引用的對(duì)象
- 本地方法棧中JNI(即一般說的Native方法)引用的對(duì)象
??從上圖中可以看到猛计,對(duì)于對(duì)象A和對(duì)象B來說爆捞,他們是屬于不可達(dá)的對(duì)象,在GC的時(shí)候是會(huì)被回收的煮甥,而對(duì)于其他的對(duì)象來說,都還在被使用卖局,所以不會(huì)被回收。
2.3 java中的引用類型
從JDK1.2之后砚偶,Java對(duì)引用的概念進(jìn)行了擴(kuò)充洒闸,將引用分為強(qiáng)引用(Strong Reference)、軟引用(Soft Reference)丘逸、弱引用(Weak Reference)、虛引用(Phantom Reference)仲锄,這四種引用的強(qiáng)度一次逐漸減弱
- 強(qiáng)引用就是指在程序代碼之中普遍存在的,類似 “Object obj = new Object()” 這類的引用昼窗,只要強(qiáng)引用還存在,垃圾回收器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象澄惊。
- 軟引用是用來描述一些還有用但并非需要的對(duì)象,對(duì)于軟引用關(guān)聯(lián)著的對(duì)象掸驱,在系統(tǒng)將要發(fā)生內(nèi)存異常之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收毕贼,如果這次回收還沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存異常
- 弱引用也是用來描述非必需對(duì)象的陶贼,但是它的強(qiáng)度比軟引用更弱一些待秃,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前,當(dāng)垃圾收集器工作時(shí)章郁,無論當(dāng)前內(nèi)存釋放足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象
- 虛引用也稱為幽靈引用或者幻影引用聊替,它是最弱的一種引用關(guān)系,一個(gè)對(duì)象是否有虛引用的存在惹悄,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響肩钠,也無法通過虛引用來取得一個(gè)對(duì)象實(shí)例,對(duì)一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知
對(duì)于方法區(qū)的回收問題:
??方法區(qū)的回收主要回收:廢棄常量和無用的類爷速。
無用的類的判定:
1.該類所有的實(shí)例都已經(jīng)被回收;
2.加載該類的ClassLoader已經(jīng)被回收惫东;
3.改類對(duì)應(yīng)的java.lang.Class對(duì)象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法颓遏。