JVM內存管理筆記

這篇文章經過翻譯、總結,提煉SUN公司(已被甲骨文收購) 的文檔 "Memory Management in the Java HotSpot™ Virtual Machine", Sun Microsystems, April 2006"java

獲得的JVM內存管理方面的學習筆記。其中大部分插圖來自文檔。 原官方文檔 查閱原文算法

1.內存的手動、自動管理安全

內存的手動管理,須要明確地指出對象什麼時候再也不被使用並將其釋放,而自動管理,須要根據對象的引用狀況來判斷釋放與否。固然,被引用的對象不必定都有用;混亂的引用致使了一些對象不會被釋放。自動管理解決了大部份內存問題,但沒法徹底解決內存問題。服務器

2.垃圾回收的概念網絡

垃圾回收器(Garbage Collector, GC) 是JVM 進行內存管理的工具。多線程

垃圾回收器負責:併發

  •  分配內存
  •  保存所有被引用的對象在內存中
  •  在代碼執行時回收再也不被引用的對象所佔用的內存

Java程序中被引用的對象(應該指被有效引用的對象)稱爲存活的(live). 再也不被引用的對象稱爲死亡的(dead) 也稱做垃圾(garbage). 對死亡的對象進行查找並釋放內存空間的過程被稱爲垃圾回收。oracle

  2.1.什麼是良好的垃圾回收器?工具

  • 安全並高效地的分配與釋放內存空間

安全即有用的數據不會被回收,無用的數據不會長期得不到釋放;高效即回收器工做時不會引發長時間的卡頓。性能

然而,大多數系統都有時間,空間和頻率之間的權衡。堆小會提高回收速度,但堆很快會被填滿,形成更頻繁地回收。

  • 碎片限制

在對象的被釋放後,回收獲得的空間多是分散在不一樣區塊的碎片,這些碎片可能都沒有足夠的空間來分配一個大對象。咱們須要消除碎片得到連續的空間,消除碎片的方法成爲「壓實」(compaction)

  • 可擴展性

分配與回收是否成爲多線程應用程序在多處理器系統上擴展的瓶頸。

  2.2.垃圾回收算法要權衡什麼?

設計或選擇一個垃圾回收算法應作出以下選擇:

  • 串行(Serial)與並行(Parallel)

串行則只有一個CPU進行回收,並行則將回收任務被分割交由不一樣的CPU同時執行。並行雖迅速,但會產生額外的複雜性與潛在的碎片。

  • 同步(Concurrent)與「暫停」(Stop-the-world)

 「暫停」則回收執行時,應用程序的將徹底暫停執行。同步則經過增大開銷來縮短暫停時間以改善性能,同步可與應用程序同時進行,提升響應速度,但面對對象更新時要格外當心。

  • 「壓實」 (Compacting)與「非密實」 (Non-compacting)與複製(Copying)

「壓實」則在回收工做後將全部存活的移動至一塊兒,徹底回收剩餘內存。以後將指針指向第一個自由位置,以便快速分配對象,以後更新自由位置用於下一次分配。「非密實」在對象釋放後不進行處理,以快速完成回收任務,所以會產生潛在碎片。複製能夠將存活的對象「疏散」到不一樣的內存區域,原區能夠被快速利用進行下一次分配,但其須要額外的時間和空間。

3.從J2SE 5.0 HotSpot JVM起的四種垃圾回收器

  3.1.HotSpot JMM概況

HotSpot JMM 存在三個區塊:年輕代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation), 大部分對象在初始化後被分配到年輕代的Eden Space, 當Eden Space 將飽和時,經過Survivor Space 進行回收,屢次回收後倖存的對象會被移動到年老代上的養老區(Tenured Space). 經歷屢次年輕代垃圾回收後的倖存對象被分配到年老代。空間佔用大的對象能夠直接分配進入年老代。持久代通常存儲可被垃圾回收器直接識別爲持久的對象,例如類和方法以及描述類。

3.2.垃圾回收的類型

年輕代將飽和時,執行年輕代回收(Young Generation Collection), 亦稱小回收(Minor Collection). 年老代或持久代將飽和時,執行全面回收(Full Collection),亦稱主回收(Major Collection) 對全部代對象的進行回收。一般,年輕代的對象先被回收,爲保障高效,年輕代回收使用特定的算法。而年老代和持久代則使用相同的算法。

3.3.Serial Collector (SC 串行回收器)

SC由單個線程負責執行,回收時應用程序的執行將被暫停。

  • SC的年輕代回收機制:

回收執行時,在年輕代的Eden Space 中存活的對象被複制到Survivor Spaces 的To Space 中,但若是該對象大小超過To Space 的空間,它將被直接複製到年老代。Survivor Spaces 中From Space 存活的對象仍然被認爲是年輕的,所以也會被複制到To Space 中,直到對象被標記爲年老時纔會被複制到年老代。當To Space 快滿時,Eden Space 和From Space 中的倖存對象不會被複制進去,不管經歷了多少年輕代的垃圾回收。當Eden Space 和From Space 中倖存對象全被複制後,遺留對象(圖中標記爲紅色X)所有被標記爲死亡,再也不被檢查與標記。年輕代回收完畢後,Eden Space 和From Space 將清空;若有沒進入年老代的對象,那它只能在To Space 中,此時From Space 和To Space 交換角色。

  • SC的年老代回收機制:

SC在年老帶中採用mark-sweep-compact算法進行垃圾回收。

(1) 標記(mark):判斷哪些對象仍然存活並進行標記

(2) 掃描(sweeps):經過對若干代的掃描肯定垃圾

(3) 「滑動—壓實」(sliding-compaction):對倖存對象進行壓縮,以使他們在連續的空間上有序並至地排列。「壓實」操做後,碎片被整合集中,使新進入的對象可使用凹凸指針(bump-the-pointer)進行快速地址分配。

  • SC的應用

多用於客戶機(client-style). 程序能夠容忍短暫地暫停。在J2SE 5.0以後的版本中默認開啓。

指令配置:-XX:+UseSerialGC

3.4.Parallel Collector (PC 並行回收器)

PC是高吞吐量回收器(Throughput Collector), 具備充分利用核心的優點。當前應用普遍。PC是SC的增強版本。

  • PC的年輕代回收機制:

PC與SC採用相同的方式對年輕代進行垃圾回收,還是一個「‘暫停—複製’回收器」,但並行下降了時間開銷,提升了應用的吞吐量。

  • PC的年老代回收機制:

與SC相同,採用mark-sweep-compact回收算法提高效率,但仍有罕見狀況使年老代的回收形成程序較長時間的暫停。

  • PC的應用

多用於服務器(server-class). 在J2SE 5.0以後的版本中默認開啓。

指令配置:-XX:+UseParallelGC

3.5.Parallel Compacting Collector (PCC 並行壓縮回收器)

與PC相同PCC在年老代垃圾回收採用了新算法,並最終取代PC

  • PCC的年輕代回收機制:

PCC與PC採用相同的方式對年輕代進行垃圾回收。

  • PCC的年老代回收機制

PCC的年老帶回收會形成應用暫停。

(1) 標記(mark):在邏輯層面上將年老代劃分紅大小固定的分區。經過多線程先標記根集合中能夠直接訪問的對象,再標記剩餘對象。只要區中有幸存對象,該區的大小和位置信息將會被採集更新。

(2) 總結(summary):該方法是對區進行操做,而非操做對象。一般狀況下,當進行年老代垃圾回收時,因爲前某一代的「壓實」操做,使得當前內存空間中,左邊的存儲密度仍然很大,甚至左邊的對象幾乎是連續排列的。從高密度區中回收零星的碎片空間可能不值得再次壓實」形成的開銷。因此,總結會從最左端檢查區的密度(依據標記後區的信息採集結果), 直到到達一個點,從該點最右端進行「壓實」回收內部的碎片空間能夠彌補開銷。這個點被成爲「密度前綴」(dense prefix), 它左邊的對象均不會被位移而右邊的空間均會被壓縮回收。

總結方法實際是串行實現的,固然也能夠並行,但並無標記和「壓實」的並行實現那樣重要。

(3) 「壓實」(compaction):多個線程基於總結操做的結果,肯定須要被填滿的區,而後獨立複製數據進行填充。最後,堆的內存分配是一端十分密集,另外一端幾乎爲空。

  • PCC的應用

多線程的機器,但不適合大型共享機器。

應用前需指明。

指令配置:-XX:+UseParallelOldGC

3.6.Concurrent Mark-Sweep (CMS) Collector (CMSC 併發標記回收器)

在不少應用場景中,快速響應(fast response time) 比端到端的吞吐量(end-to-end throughput) 更重要。若是使用了大堆,年老代的回收可能形成長時間停頓。

HotSpot JVM 引入了CMSC 亦做「低延遲迴收器」(low-latency collector) 來解決這一問題。

  • CMSC的年輕代回收機制:

    CMSC與PC採用相同的方式對年輕代進行垃圾回收。

  • CMSC的年老代回收機制:

大多數的CMSC垃圾回收是在應用程序執行時完成的。

(1) 初始標記(initial mark):應用暫停,標記根集合中能夠直接訪問的對象,得到倖存對象的初始集合。

(2) 併發標記(concurrent mark):標記初始集合中所有被間接引用的對象。

(3) 備註/再標記(remark):因爲併發標記不會暫停應用,沒法保證被更新的引用都獲得標記。爲此,備註過程再次暫停應用,對併發標記時被修改的對象進行標記,以保障垃圾回收的完整性。因爲備註所需的暫停時長遠大於初始標記的暫停時長,採用多線程來提升效率。

 

 

 

 

 

 

 

 

 

 

 

 

 

(4) 掃描(sweep):CMSC加大備註過程的開銷來減小暫停時長,而且是惟一沒有「壓實」操做的回收器,即不會將倖存的對象有序並至排列。

雖然非「壓實」節約時間,但因爲空閒內存不連續,回收器沒法使用凹凸指針技術分配新對象。爲此,CMSC用多個鏈式隊列(應該是對碎片依據空間大小分類存放)碎片,根據新對象的大小經過鏈式隊列分配適當位置。所以,相比於其餘回收器,CMSC在年老代的資源分配成本更高,也須要開闢更大的堆。

另外,儘管老年代的每次回收對所有對象進行清洗,但以後的「新生垃圾」,亦做浮動垃圾(floating garbage), 只能在下一次老年代回收時被清理。因爲沒有「壓實」,碎片早晚會積累,爲此CMSC對活躍對象進行跟蹤(track), 經過估算空間需求來分割或合併碎片空間。

  • CMSC的應用:

CMSC適用於要求暫停時間短,而且能夠承擔與回收器共享處理器資源的應用。一般適用於多線程,老年代數據集較大的應用,如網絡服務器。

應用前需指明。

指令配置:-XX:+UseConcMarkSweepGC       -XX:+CMSIncrementalMode (incremental mode)

相關文章
相關標籤/搜索