Java虛擬機(jī)(JVM) 之 GC

其實(shí)我以前看到提到 JVM 之類的東西,就覺得這玩意太難讯检,看不下去啊⌒粒可是要進(jìn)階吧视哑,又是必須的啦,所以說(shuō)一句 干就完了


image

今天就來(lái)學(xué)學(xué) GC誊涯,想問就我一個(gè)人想到中文的滾粗嗎


image

其實(shí)這么理解也可以啦,GC 是什么意思呢蒜撮,可以大概這么理解 公廁只有這么些個(gè)暴构,偏偏有些人占著茅廁不那啥跪呈,只好讓管理員讓它滾粗啦,好讓其它人可以用取逾,讓世界和平耗绿。

好了好了 回來(lái)了回來(lái)了,為什么會(huì)有 GC (回收垃圾砾隅,釋放內(nèi)存)呢误阻?因?yàn)閮?nèi)存就這么大,用完了不回收晴埂,就一直占用著究反,可以用的內(nèi)存就越來(lái)越小,到最后溢出了(OOM)儒洛,程序也就GG了精耐。

那么問題來(lái)了,怎么知道這個(gè)占用著的對(duì)象是不是垃圾呢琅锻?

一:引用計(jì)數(shù)法

給對(duì)象添加一個(gè)引用計(jì)數(shù)器卦停,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器就加1恼蓬,當(dāng)引用失效時(shí)惊完,計(jì)數(shù)器就減 1,任何時(shí)刻計(jì)數(shù)器為 0 的對(duì)象就是不可能再被使用的处硬,就是垃圾了小槐,可以回收了。(但是主流的虛擬機(jī)并不是用這種方法)

二:可達(dá)性分析算法

講解這個(gè)之前郁油,我覺得還是要先知道 Java 運(yùn)行時(shí)的內(nèi)存區(qū)域是怎么區(qū)分的本股。自行百度?哈哈
網(wǎng)上找了張圖:


image

什么意思呢桐腌?
意思就是拄显,這個(gè)算法呢就是通過一系列稱為 “GC Roots” 的對(duì)象作為起點(diǎn),從這些起點(diǎn)向下搜索案站,搜索走過的路徑稱為 引用鏈躬审,當(dāng)一個(gè)對(duì)象到 GC Roots 沒有引用鏈的時(shí)候,我們就可以指著它的鼻子罵“垃圾”蟆盐,等著被回收吧承边?

那么就有人懵逼了 GC Roots 是什么玩意啊,我不知道啊

舉個(gè)我認(rèn)為的例子:看過戰(zhàn)狼2的都知道石挂,有了中國(guó)的護(hù)照博助,我們就是祖國(guó)花朵(哈哈),到哪都是中國(guó)人的身份痹愚,那么國(guó)家就是我們最強(qiáng)的后盾富岳。那么我們就可以把 中國(guó)理解為 GC Roots,護(hù)照就是篩選是否是中國(guó)人的 引用鏈蛔糯,有就有中國(guó)做我們的后盾,沒有就等著被回收(沒被的意思....)

那么在程序中 什么可以作為 GC Roots呢窖式?
  • 虛擬機(jī)棧中的引用
  • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
  • 方法區(qū)中常量的引用對(duì)象
  • 本地方法棧中 JNI(Native) 引用的對(duì)象
什么時(shí)候回收

不同的虛擬機(jī)實(shí)現(xiàn)有著不同的 GC 實(shí)現(xiàn)機(jī)制蚁飒,但是一般情況下每一種 GC 實(shí)現(xiàn)都會(huì)在以下兩種情況下觸發(fā)垃圾回收。
Allocation Failure:在堆內(nèi)存中分配時(shí)萝喘,如果因?yàn)榭捎檬S嗫臻g不足導(dǎo)致對(duì)象內(nèi)存分配失敗淮逻,這時(shí)系統(tǒng)會(huì)觸發(fā)一次 GC。
System.gc():在應(yīng)用層阁簸,Java 開發(fā)工程師可以主動(dòng)調(diào)用此 API 來(lái)請(qǐng)求一次 GC爬早。

那么這里就引出了 引用 的概念,全部有4種引用
  • 強(qiáng)引用:
    就是代碼中直接 new 對(duì)象的引用强窖,這種是永遠(yuǎn)不會(huì)被回收的
  • 軟引用:
    用來(lái)描述一些非必須的對(duì)象凸椿,弱引用的對(duì)象 將在內(nèi)存將要發(fā)生內(nèi)存溢出異常之前,會(huì)被列進(jìn)回收范圍進(jìn)行二次回收翅溺,如果回收之后內(nèi)存還不夠脑漫,才報(bào)溢出異常
  • 弱引用:
    也是用來(lái)描述一些非必須的對(duì)象,但是它是 無(wú)論內(nèi)存夠不夠都會(huì)被會(huì)回收
  • 虛引用:
    僅持有虛引用的對(duì)象咙崎,在任何時(shí)候都可能被GC优幸,設(shè)置虛引用的唯一目的:就是能在這個(gè)對(duì)象唄回收時(shí)收到一個(gè)系統(tǒng)通知。

注意:即使在可達(dá)性分析算法中不可達(dá)的對(duì)象褪猛,也不是 非死不可的网杆,這時(shí)候它們暫時(shí)處于“緩刑”,真正死亡伊滋,至少要經(jīng)歷兩次標(biāo)記的過程:判斷對(duì)象是否有必要執(zhí)行finalize()方法碳却;若被判定為有必要執(zhí)行finalize()方法,之后還會(huì)對(duì)對(duì)象再進(jìn)行一次篩選笑旺,如果對(duì)象能在finalize()中重新與引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián)昼浦,將被移除出“即將回收”的集合。任何對(duì)象的 finalize()方法筒主,只會(huì)執(zhí)行一次关噪。

public class FinalizeEscapeGC {

    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive() {
        System.out.println("yes, i am still alive :)");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize mehtod executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws Throwable {
        SAVE_HOOK = new FinalizeEscapeGC();

        //對(duì)象第一次成功拯救自己
        SAVE_HOOK = null;
        System.gc();
        // 因?yàn)镕inalizer方法優(yōu)先級(jí)很低,暫停0.5秒乌妙,以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead :(");
        }

        // 下面這段代碼與上面的完全相同使兔,但是這次自救卻失敗了
        SAVE_HOOK = null;
        System.gc();
        // 因?yàn)镕inalizer方法優(yōu)先級(jí)很低,暫停0.5秒藤韵,以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead :(");
        }
    }
}
#################
運(yùn)行結(jié)果:
finalize mehtod execute!
yes, i am still alive :)
no, i am dead :(
兩段代碼是一樣的虐沥,執(zhí)行結(jié)果一次成功,一次失敗泽艘,這就是上面提到的任何對(duì)象的 finalize()方法置蜀,只會(huì)執(zhí)行一次奈搜。

那么問題來(lái)了悉盆,知道是垃圾后盯荤,要怎么去回收呢?當(dāng)然就是每個(gè)人都會(huì)說(shuō)的垃圾收集算法啦

  • 標(biāo)記 — 清除算法

顧名思義焕盟,是垃圾 就 標(biāo)記秋秤,然后把標(biāo)記的給 清除 就好了(標(biāo)記過程就是上面講的)
缺點(diǎn):效率,空間問題脚翘。標(biāo)記清除后會(huì)產(chǎn)生大量的不連續(xù)內(nèi)存碎片灼卢。

為啥會(huì)有這樣的缺點(diǎn),不理解?舉個(gè)例子:

你種了好多大白菜来农,一眼看過去鞋真,好多個(gè)點(diǎn)都有壞死的,而且都不是同一個(gè)區(qū)域沃于,你依次走過去涩咖,標(biāo)記是要扔掉的,然后就去清理了繁莹,這樣下來(lái)效率就很慢了檩互,而且你拔掉的地方那么小,翻土重新種又太小咨演,容易弄壞其他的白菜闸昨,只能空著,那么就是空間問題了薄风。

復(fù)制算法

把可用內(nèi)存按容量劃分為大小相等的兩塊饵较,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用盡后遭赂,把還存活著的對(duì)象 復(fù)制 到另外一塊上面循诉,再將這一塊內(nèi)存空間一次清理掉。
例子:這個(gè)就更容易理解了嵌牺,叫了兩打啤酒打洼,每次喝都隨機(jī)兩打里面拿,要結(jié)賬的時(shí)候逆粹,把空瓶裝一箱募疮,沒喝存起來(lái)的裝一箱,結(jié)賬時(shí)候清理空瓶的那一箱就好了僻弹。
缺點(diǎn):為了解決效率問題阿浓。代價(jià) 內(nèi)存縮小了一半

標(biāo)記 — 整理算法

首先 標(biāo)記 出所有需要回收的對(duì)象,然后進(jìn)行 整理蹋绽,使得存活的對(duì)象都向一端移動(dòng)芭毙,最后直接清理掉端邊界以外的內(nèi)存筋蓖。
例子 :平常我們自己整理房間就是啦,雜亂的房間中 不想要的扔掉退敦,不要讓它繼續(xù)堆放在房間占位置粘咖,把空出來(lái)的存放自己想要的東西,
優(yōu)點(diǎn):即沒有浪費(fèi)50%的空間侈百,又不存在空間碎片問題瓮下,性價(jià)比較高。
一般情況下钝域,老年代會(huì)選擇標(biāo)記-整理算法讽坏。

分代收集算法

根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊。一般是把 JAVA 堆分為新生代和老年代例证,然后根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?br> 新生代:回收時(shí)路呜,有大批對(duì)象死去,只有少量存活织咧,適合復(fù)制算法胀葱。
老年代:存活率高,沒有額外的分配擔(dān)狈掣校空間巡社,必須使用 標(biāo)記 — 清理,或 標(biāo)記 — 整理手趣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末晌该,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子绿渣,更是在濱河造成了極大的恐慌朝群,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件中符,死亡現(xiàn)場(chǎng)離奇詭異姜胖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)淀散,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門右莱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人档插,你說(shuō)我怎么就攤上這事慢蜓。” “怎么了郭膛?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵晨抡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)耘柱,這世上最難降的妖魔是什么如捅? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮调煎,結(jié)果婚禮上镜遣,老公的妹妹穿的比我還像新娘。我一直安慰自己汛蝙,他們只是感情好想许,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布逢捺。 她就那樣靜靜地躺著缩筛,像睡著了一般岂座。 火紅的嫁衣襯著肌膚如雪笤受。 梳的紋絲不亂的頭發(fā)上喻犁,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天陆错,我揣著相機(jī)與錄音捞蚂,去河邊找鬼鞍盗。 笑死需了,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的般甲。 我是一名探鬼主播肋乍,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼敷存!你這毒婦竟也來(lái)了墓造?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锚烦,失蹤者是張志新(化名)和其女友劉穎觅闽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涮俄,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛉拙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彻亲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孕锄。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖苞尝,靈堂內(nèi)的尸體忽然破棺而出畸肆,到底是詐尸還是另有隱情,我是刑警寧澤野来,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布恼除,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏豁辉。R本人自食惡果不足惜令野,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望徽级。 院中可真熱鬧气破,春花似錦、人聲如沸餐抢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旷痕。三九已至碳锈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間欺抗,已是汗流浹背售碳。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绞呈,地道東北人贸人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像佃声,于是被迫代替她去往敵國(guó)和親艺智。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354