摘要:暫停整個應用復制收集將堆內存分成兩個相同空間,從根類似于前面的有向圖起始頂點開始訪問每一個關聯的可達對象,將空間的全部可達對象復制到空間,然后一次性回收空間。如果老年代空間也不夠或者達到觸發老年年垃圾回收條件的話則觸發一次。
[TOC]
引用計數一般來說,每個對象對應一個計數器,創建對象時,將其計數器置0
當對象唄賦予任意變量時,引用計數器每次加1.
引用變量出了作用域后,該引用變量所引用的對象的計數器減1.
一旦引用計數器為0,對象就滿足垃圾收集的條件
優點:基于引用計數器的垃圾收集器運行較快,不會長時間中斷程序執行,適宜必須實時運行的程序。
缺點:引用計數器增加了程序執行的開銷,因為每次對象賦予給新的變量,計數器加1,而每次引用變量出了 作用域后,該引用的對象的計數器減1
還無法解決環形引用
Class A(){ public B b = null; } Class B(){ public A a = null; } public void test(){ A a = new A(); B b = new B(); a.b=b; b.a = a }標記-清除
mutator:除了垃圾收集器之外的部分,比如說我們的應用程序本身。職責(分配內存),read(從內存中讀取內容),write(將內容寫入內存)
collector:回收不再使用的內存,供mutator 使用new分配內存
mutator roots:一般指的是分配在堆內存之外,可以直接被mutator直接訪問到的對象
標記階段:collector從mutator根對象開始進行遍歷,能被mutator roots訪問到的對象都打上一個標識,記錄為可達對象
清除階段:collector對內存從頭到尾進行線性變量,如果某個對象沒有標記為可達對象,就將其回收
從上圖我們可以看到,在Mark階段,從根對象1可以訪問到B對象,從B對象又可以訪問到E對象,所以B,E對象都是可達的。同理,F,G,J,K也都是可達對象。到了Sweep階段,所有非可達對象都會被collector回收。同時,Collector在進行標記和清除階段時會將整個應用程序暫停(mutator),等待標記清除結束后才會恢復應用程序的運行
缺點:標記-清除算法的比較大的缺點就是垃圾收集后有可能會造成大量的內存碎片,像上面的圖片所示,垃圾收集后內存中存在三個內存碎片,假設一個方格代表1個單位的內存,如果有一個對象需要占用3個內存單位的話,那么就會導致Mutator一直處于暫停狀態,而Collector一直在嘗試進行垃圾收集,直到Out of Memory。暫停整個應用
復制收集將堆內存分成兩個相同空間,從根(類似于前面的有向圖起始頂點)開始訪問每一個關聯的可達對象,將空間A的全部可達對象復制到空間B,然后一次性回收空間A。對于該算法而言,因為只需訪問所有的可達對象,將所有的可達對象復制走之后就直接回收整個空間,完全不用理會不可達對象,所以遍歷空間的成本較小,但需要巨大的復制成本和較多的內存。
缺點:需要兩倍內存空間。
優點:不會出現碎片
標記-壓縮此算法結合了“標記-清除”和“復制”兩個算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用對象,第二階段遍歷整個堆,把清除未標記對象并且把存活對象“壓縮”到堆的其中一塊,按順序排放。此算法避免了“標記-清除”的碎片問題,同時也避免了“復制”算法的空間問題。
優點:避免了“標記-清除”的碎片問題,同時也避免了“復制”算法的空間問題。
缺點:暫停整個應用
增量收集基礎仍是傳統的標記-清除和復制算法。設計一個多進程的運行環境,比如用一個進程執行垃圾收集工作,另一個進程執行程序代碼。這樣一來,垃圾收集工作看上去就仿佛是在后臺悄悄完成的,不會打斷程序代碼的運行。
缺點:垃圾收集器在第一階段中辛辛苦苦標記出的結果很可能被另一個進程中的內存操作代碼修改得面目全非,以至于第二階段的工作沒有辦法開展。---------解決辦法: 優化算法
優點:垃圾收集工作看上去就仿佛是在后臺悄悄完成的,不會打斷程序代碼的運行。
分代收集新生代
新生代包括兩個區:Eden區和Survivor區,其中Survivor區一般也分成兩塊,簡稱Survivor1 Space 和 Survivor2 Space (或者From Space 和 To Space)。新生代通常存活時間較短,因此基于標記清除復制算法來進行回收,掃描出存活的對象,并復制到一塊新的完全未使用的空間中,對應于新生代,就是在Eden和From或To之間copy。新生代采用空閑指針的方式來控制GC觸發,指針保持最后一個分配的對象在新生代區間的位置,當有新的對象要分配內存時,用于檢查空間是否足夠,不夠就觸發GC。當連續分配對象時,對象會逐漸從eden到Survior,最后到舊生代。
回收機制:復制回收
老年代
在垃圾回收多次,如果對象仍然存活,并且新生代的空間不夠,則對象會存放在老年代。
在老年代采用的是 標記清除壓縮算法。因為老年代的對象一般存活時間比較長,每次標記清除之后,會有很多的零碎空間,這個就是所謂的浮動垃圾。當老年代的零碎空間不足以分配一個大的對象的時候,就會采用壓縮算法。在壓縮的時候,應用需要暫停。
回收機制:標記-壓縮
持久代
這部分空間主要存放java方法區的數據以及啟動類加載器加載的對象。這一部分對象通常不會被回收。所以持久代空間在默認的情況下是不會被垃圾回收的。
回收機制:不會被回收
首先想eden區申請分配空間,如果空間夠,就直接進行分配,否則進行一次Minor GC。minor GC 首先會對Eden區的對象進行標記,標記出來存活的對象。然后把存活的對象copy到From空間。如果From空間足夠,則回收eden區可回收的對象。如果from內存空間不夠,則把From空間存活的對象復制到To區,如果TO區的內存空間也不夠的話,則把To區存活的對象復制到老年代。如果老年代空間也不夠(或者達到觸發老年年垃圾回收條件的話)則觸發一次full GC。
Minor GC
一般情況下,當新對象生成,并且在Eden申請空間失敗時,就好觸發Minor GC,堆Eden區域進行GC,清除非存活對象,并且把尚且存活的對象移動到Survivor區。然后整理Survivor的兩個區。
Full GC
對整個堆進行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,因此應該盡可能減少Full GC。有如下原因可能導致Full GC
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64901.html
摘要:垃圾回收算法與垃圾回收器綜述我們常說的垃圾回收算法可以分為兩部分對象的查找算法與真正的回收方法。串行垃圾回收器一次只使用一個線程進行垃圾回收并行垃圾回收器一次將開啟多個線程同時進行垃圾回收。 垃圾回收算法與 JVM 垃圾回收器綜述歸納于筆者的 JVM 內部原理與性能調優系列文章,文中涉及的引用資料參考 Java 學習與實踐資料索引、JVM 資料索引。 showImg(https://s...
摘要:在這種消耗很高的狀態下,應用程序所有的線程都會掛起,暫停一切正常的工作,等待垃圾回收的完成。但是,因為線程切換和上下文轉換的消耗,會使得垃圾回收的總體成本上升,造成系統吞吐量的下降。 Java 垃圾回收(GC) 泛讀 文章地址: https://segmentfault.com/a/1190000008922319 0. 序言 帶著問題去看待 垃圾回收(GC) 會比較好,一般來說主要的...
垃圾回收(GC)是JVM的一大殺器,它使程序員可以更高效地專注于程序的開發設計,而不用過多地考慮對象的創建銷毀等操作。但是這并不是說程序員不需要了解GC。GC只是Java編程中一項自動化工具,任何一個工具都有它適用的范圍,當超出它的范圍的時候,可能它將不是那么自動,而是需要人工去了解與適應地適用。 擁有一定工作年限的程序員,在工作期間肯定會經常碰到像內存溢出、內存泄露、高并發的場景。這時候在應對這...
摘要:這個算法看似不錯而且簡單,不過存在這一個致命傷當兩個對象互相引用的時候,就永遠不會被回收于是引用計數算法就永遠回收不了這兩個對象,下面介紹另一種算法。 前言 ? 如果要問Java與其他編程語言最大的不同是什么,我第一個想到的一定就是Java所運行的JVM所自帶的自動垃圾回收機制,以下是我學習JVM垃圾回收機制整理的筆記,希望能對讀者有一些幫助。 哪些內存需要回收?what? ? ...
閱讀 953·2021-11-24 09:39
閱讀 2689·2021-09-26 09:55
閱讀 14154·2021-08-23 09:47
閱讀 3577·2019-08-30 15:52
閱讀 849·2019-08-29 13:49
閱讀 997·2019-08-23 18:00
閱讀 844·2019-08-23 16:42
閱讀 1635·2019-08-23 14:28