JAVA 垃圾收集器與記憶體配置策略

來源:互聯網
上載者:User

標籤:

引言

    垃圾收集技術並不是Java語言首創的,1960年誕生於MIT的Lisp是第一門真正使用記憶體動態分配和垃圾收集技術的語言。垃圾收集技術需要考慮的三個問題是:

  1. 哪些記憶體需要回收

  2. 什麼時候回收

  3. 如何回收

    http://my.oschina.net/jiangmitiao/blog/470426 中講到java記憶體運行時地區的分布,其中程式計數器,虛擬機器棧,本地方法區都是隨著線程而生,隨線程而滅,所以這幾個地區就不需要過多考慮回收問題。但是堆和方法區就不一樣了,只有在程式運行期間我們才知道會建立哪些對象,這部分記憶體的分配和回收都是動態。垃圾收集器所關注的就是這部分記憶體。


二 對象死亡判據

    垃圾收集器在對一個對象回收之前,首先要判斷對象在程式中是否還有使用的可能性,充要條件就是沒有引用再指向這個對象執行個體。最簡單的辦法就是給對象執行個體添加中添加一個引用計數器,每當有一個引用指向它時,計數器就加一,當引用失效時,計數器就減一,如果計數器值為0則說明沒有引用指向它,可以進行回收。但是這個方法中計數器為0並不是一個必要條件,例如,產生兩個對象執行個體,每個對象執行個體的屬性都指向對方,那麼這個兩個對象執行個體分別最少有一個引用。

    java採用的是可達性分析演算法,即找一部分對象作為"GC Roots"節點,從這些節點開始向下搜尋,當某個對象到"GC Roots"節點沒有可達路徑時,說明此對象是停用。在java中作為"GC Roots"的節點包括:虛擬機器棧中引用的對象,方法區靜態屬性引用的對象,方法區常量引用的對象,本地方法區中本地調用所引用的對象。

     引用擴充    

    如果reference類型的資料中儲存的數值是另一塊記憶體的起始地址,那麼這塊記憶體就代表著一個引用。一個對象在這種狀態下,只能有被引用和沒有被引用兩種狀態。java對引用概念進行了擴充,將引用分為強引用(new),軟引用(softReference),弱引用(WeakReference),虛引用(PhantomReference)。如果強引用存在,則垃圾收集器不會回收該對象。如果系統即將發生記憶體溢出異常,那麼記憶體回收集器則會回收這些對象。弱引用對象只能存活到下一次垃圾收集之前。虛引用對象不會對其存留時間構成任何影響。

    對象的自我救贖

    在垃圾收集器發現某一個對象到"GC Roots"路徑不可達時,先會判斷該對象是否覆蓋finalize()方法,或是否執行過finalize()方法。如果覆蓋了且沒有執行過該方法,則會將該對象放到低優先順序的Finalizer線程中去執行finalize()方法,如果在finalize()方法中該對象又被引用,則會逃脫被回收的命運。

    方法區的回收

    方法區中主要回收廢棄的常量和無用的類。對於常量,如果沒有引用指向常量,則該常量會被回收。對於類的回收則麻煩許多,首先要判斷該類是無用的類,無用的類要滿足三個條件:1所有類的執行個體被回收2載入該類的ClassLoader已經被回收3Class沒有被引用,不會通過反射訪問該類的方法。


三 記憶體回收演算法    標記-清除演算法(Mark-Sweep)

    該演算法分為兩個階段:首先標記處要回收的對象,標記完成後統一回收所有被標記的對象。

    存在的問題:1 標記和清除效率都不高 2 標記清除後會產生大量記憶體片段,分配大對象時可能觸發另一次垃圾收集。

    複製演算法(Copying)

    該演算法將記憶體分為兩個等大小的地區,每次只使用一個地區。當一個地區快用完了,就將這個地區中存活的對象複製到另一個地區

    優點是避免了記憶體片段的產生,缺點是浪費記憶體空間。

    有公司研究表明,新生代的對象98%都是朝生暮死,所以虛擬機器把新生代記憶體劃分為一個較大的Eden空間和兩個較小的Survivor空間。每次只是用Eden空間和一個Survior空間,當進行複製清理時,將Survivor空間和Eden空間中存活的對象複製到另一塊Survivor空間。當Survivor空間不夠用時,就會依賴老年代進行分配擔保。

    標記-整理演算法(Mark-Compact)

    針對老年代對象存活率高的情況,複製演算法明顯不合適,於是採用標記整理演算法,標記和標記清除演算法相同,二後邊的整理則是讓所有存活的對象都向一端移動,然後清理掉邊界外的記憶體。

    分代收集

    當前虛擬機器都採用分代收集,分帶的依據是對象的存活周期。一般新生代存活率低,採用複製演算法。老年代存活率高採用標記整理或標記清除。


四 垃圾收集器

由於虛擬機器採用了分代收集,所以針對不同代收集器也不同。是HotSpot虛擬機器的垃圾收集器,連線表示可以協同工作。

Serial收集器,複製演算法,它是一個單線程的收集器,並且在進行收集時會暫停其他線程,它預設是client模式下的新生代收集器。

ParNew收集器是Serial收集器的多線程版,它是第一款並發收集器。

Parallel Scavenge收集器可以精確控制輸送量(使用者代碼已耗用時間/(使用者代碼時間+垃圾收集時間))


SerialOld收集器是serial收集器的老年版,採用標記整理演算法,同樣是單線程收集器。

ParallelOld是ParallelScavenge收集器的老年版,使用多線程和標記整理演算法。

CMS收集器是以最短回收停頓時間為目標的收集器,採用標記清除演算法,在重視響應速度的系統中得以應用。但是缺點是對CPU資源敏感,無法處理浮動垃圾,易產生記憶體片段。


G1收集器是最新推出的收集器,可應用在JDK1.7u4及以上版本。它將記憶體分為多個Region,新生代和老年代分別包含多個Region。G1跟蹤各個Region,判斷垃圾價值大小,優先回收價值最大的Region。


五 記憶體配置與回收策略

    對象的分配,就是在堆上分配,對象主要分配在新生代的Eden地區中,如果啟動了本地線程分配緩衝,則按線程優先在TLAB中分配。少數情況也有可能直接分配到老年代。

    對象在Eden地區分配時,當Eden地區沒有足夠空間,虛擬機器會發起一次新生代垃圾收集。

    如果對象需要大量連續記憶體空間,例如String類型和數組。大對象對於虛擬機器記憶體配置來說是一個壞訊息,朝生暮死的大對象是要命的壞訊息。經常出現大對象會導致多次出發垃圾收集。對於這類對象,可以設定參數將大對象直接存入老年代。

    每一個對象都有一個年齡計數器,當對象在Eden地區出生,每經過一次GC,並且存入Survivor,計數器加一。當年齡增加到一定程度(預設15),則會被存入老年代。同時,如果Survivor空間中相同年齡對象占空間超過50%,則也會直接進入老年代。

    

總結

垃圾收集演算法:複製演算法,標記-清除演算法,標記-清理演算法。

垃圾收集器特點:新生代用複製,老年代用標記清理,CMS用標記清除。

Eden空間大小和Survivor空間大小預設比率為8:1,即新生代10%的空間用來存放複製後的對象。



JAVA 垃圾收集器與記憶體配置策略

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.