深入理解Java虛擬機器(四)-垃圾收集演算法__編碼

來源:互聯網
上載者:User
概述

當前的商業虛擬機器的垃圾收集都採用“分代收集”(Generational Collection)演算法,這種演算法並沒有什麼新的思想,只是根據對象存活周期的不同將記憶體劃分為幾塊。一般是把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。在新生代中,每次垃圾收集時都發現有大批的對象死去,只有少量存活,那就選用複製演算法,只需要付出少量存活對象的複製成本就可以完成收集。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記-清理”或者“標記-整理”演算法來進行回收。

新生代(Young Generation)

新生代是所有新對象產生的地方。當Eden區沒有足夠空間進行分配時,虛擬機器將發起一次Minor GC。年輕代分為3個部分:Enden區和Survivor From和Survivor To區。
年輕代的主要特點: 大多數建立的對象都位於Eden區。 當Eden區被對象填滿時,就會執行Minor GC。並把所有存活下來的對象轉移到其中一個Survivor 區。 Minor GC同樣會檢查存活下來的對象,並把它們轉移到另一個Survivor 區。這樣在一段時間內,總會有一個空的Survivor 區。 “熬過”多次Minor GC後,仍然存活下來的對象會被轉移到年老代記憶體空間。如果對象在Eden出生並經過第一次Minor GC後任然存活,並且能被Survivor 容納的話,它將被移動到Survivor 空間中,並且對象年齡設為1。對象在Survivor 區中每熬過”一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度(預設為15歲),就將會被晉陞到老年代中。

新生代大小可以通過參數 -Xmn10M來控制;Eden區和Survivor區的大小可以通過參數 -XX:SurvivorRatio來進行控制,預設為8:1。 年老代(Old Generation)

年老代包含了長期存活的對象和經過多次Minor GC後依然存活下來的對象。通常會在老年代記憶體被佔滿時進行記憶體回收。老年代的垃圾收集叫做Major GC,Major GC會花費更多的時間。

Minor GC與Major GC的區別 新生代GC(Minor GC):指發生在新生代的垃圾收集動作,因為Java對象大多具備朝生夕滅的特性,所以Minor
GC非常頻繁,一般回收速度也比較快。 老年代GC(Major GC/Full GC):指發生在老年代的垃圾收集工作,出現了Major GC,經常會伴隨至少一次的Minor
GC(但並非絕對的,在Parallel Scavenge收集器的收集策略裡就有直接進行Major GC的策略選擇過程),Major
GC的速度一般會比Minor GC慢10倍以上。 垃圾收集演算法 標記-清除演算法(Mark-Sweep)

原理:
標記-清除演算法(Mark-Sweep)分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收所有被標記的對象。
缺點:
1.效率問題,標記和清除兩個過程的效率都不高;
2.空間問題,標記清除後會產生大量的不連續的記憶體片段,空間片段太多可能會導致以後再程式運行過程中需要分配較大對象時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作。 複製演算法

原理:
它將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的記憶體用完了,就將還存活著的對象複製到另外一塊上面,然後再把已經使用過的記憶體一次清理掉。這樣使得每次都是對整個半區進行記憶體回收,記憶體配置時也就不用考慮記憶體片段等複雜情況,只要移動堆頂指標,按順序分配記憶體即可,實現簡單,運行高效。
缺點:
需要複製,效率降低、浪費空間。

現在的商業虛擬機器都採用這種收集演算法來回收新生代。IBM公司的專門研究表明,新生代中對象98%都是“朝生夕死”的,所以並不需要按照1:1的比例來劃分記憶體空間,而是將記憶體分為一塊較大的Eden空間和兩塊較小的Survivor空,每次使用Eden和其中一塊Survivor。當回收時,將Eden和Survivor中還存活著的對象一次性地複製到另外一塊Survivor空間上,最後清理掉Eden和剛才用過的Survivor空間。 標記-整理演算法(Mark-Compact)

原理:
標記過程任然與“標記-清除”演算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活對象都向一端移動,然後直接清理掉端邊界以外的記憶體。 分代收集演算法(Generational Collection)

分代的記憶體回收策略,是基於這樣一個事實:不同的對象的生命週期是不一樣的。因此,不同生命週期的對象可以採取不同的收集方式,以便提高回收效率。 垃圾收集器

如果說收集演算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實現。Java虛擬機器規範對垃圾收集器應該如何?並沒有任何規定,因此不同的廠商,不同版本的虛擬機器所提供的垃圾收集器都可能會有很大差別,並且一般都會提供參數供使用者根據自己的應用特點和要求組合出各個年代所使用的收集器。這裡討論的收集器基於JDK1.7 Update 14之後的HotSpot虛擬機器(在這個版本中正式提供了商用的G1收集器,之前G1仍處於實驗狀態),這個虛擬機器包含的所有收集器如下圖所示:



上圖展示了7種作用於不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用。收集器所處的地區,則表示它是屬於新生代收集器還是老年代收集器。

在介紹這些收集器各種的特性之前,我們先來明確一個觀點:雖然我們是在對各個收集器進行比較,但並非為了挑選出一個最好的收集器。因為直到目前為止還沒有最好的收集器出現,更加沒有萬能的收集器,所以我們選擇的只是對具體應用最合適的收集器。 Serial收集器

它是一個單線程的收集器,但它的“單線程”的意義並不僅說明它只會使用一個CPU或者一條收集線程去完成垃圾收集工作,更重要的是它在進行垃圾收集時,必須暫停其他所有的背景工作執行緒,直到它收集結束。 ParNew收集器

ParNew收集器其實就是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集之外,其餘行為包括Serial收集器可用的所有控制參數、收集演算法、Stop The World、對象分配規則、回收策略等都與Serial收集器完全一樣。 Parallel Scavenge收集器

Parallel Scavenge收集器是一個新時代收集器,它也是使用複製演算法的收集器,又是並行的多線程收集器。

Parallel Scavenge收集器的特點是它的關注點與其他收集器不同,CMS等收集器的關注點是儘可能的縮短垃圾收集時使用者線程暫停時間,而Parallel Scavenge收集器的目標則是達到一個可控制的輸送量(Throughout)。所謂輸送量就是CPU運行使用者代碼的時間和總耗時的比值,即輸送量=運行使用者代碼時間/(運行使用者代碼時間+垃圾收集時間)。 Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同樣是一個單線程收集器,使用“標記-整理”演算法。 Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本。 CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以擷取最短回收停頓時間為目標的收集器。 G1收集器

G1收集器 記憶體配置與回收策略

Java技術體系中所提倡的自動記憶體管理最終可歸結為自動化的解決了兩個問題:給對象分配記憶體以及回收分配給對象的記憶體。
對象的記憶體配置,往大方向上講,就是在堆上分配(但也可能經過JIT編譯後被拆散為標量類型並間接地棧上分配),對象主要分配在新生代的Eden區上,如果啟動了本地線程分配緩衝,將按線程優先在TLAB上分配。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.