深入理解java虛擬機之垃圾收集器

来源:https://www.cnblogs.com/rainple/archive/2019/05/08/10829144.html
-Advertisement-
Play Games

前言 如果說收集演算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實現。java虛擬機規範中對垃圾收集器應該如何實現並沒有任何規定,因此不同的廠商、不同的版本的虛擬機所提供的垃圾收集器都有可能會有很大的區別,並且一般都會提供參數供用戶根據自己的應用特點和要求組合出各個年代所使用的收集器。 相關 ...


 

  前言

  如果說收集演算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實現。java虛擬機規範中對垃圾收集器應該如何實現並沒有任何規定,因此不同的廠商、不同的版本的虛擬機所提供的垃圾收集器都有可能會有很大的區別,並且一般都會提供參數供用戶根據自己的應用特點和要求組合出各個年代所使用的收集器。

  相關係列博客:

  上圖中展示了不同年齡代的收集器,其中Serial、ParNew和Parallel Scavenge收集器作用於新生代,CMS、Parallel Old 和 Serial Old作用於老年代,G1在新生代和老年代都可以使用。不同的收集器之間如果有連線,則說明他們可以相互搭配使用。

  相關概念

  並行:指的是多條垃圾收集線程一起公共,但是此時用戶工作線程仍處於等待狀態。

  併發:指的是用戶線程和垃圾收集線程同時工作,也有可能是交替執行,用戶程式在繼續執行,而垃圾收集程式運行與另一個CPU上。

  吞吐量:吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 /(運行用戶代碼時間 + 垃圾收集時間)。虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。

 

  Serial收集器

  Serial收集器是一款串列執行的收集器,它是歷史最悠久,也是最基本的收集器,採用複製演算法實現的新生代收集器。在jdk1.3以前,Serial收集器是新生代唯一的選擇。它是一個單線程執行的收集器,工作時只會知用一個cpu或線程區執行,更重要的是Serial在工作期間必須停掉所有的用戶線程,直至垃圾收集完成,這一過程我們稱之為“stop the world”。這項工作是由虛擬機自動執行和自動完成的,用戶在不知情的情況下停掉了所有的線程,這對於一個最求響應速度來說簡直是無法接受的。下圖展示了Serial收集器在工作時的運行流程:

  由於Serial收集器的工作模式是單線程的,自然就沒有了多線程環境下線程切換帶來的性能開銷,所以該收集器在單線程環境下更加簡單高效。

 

  ParNew 收集器

  Parnew是Serial收集器的多線程版本,也是新生代收集器。ParNew收集器和Serial收集器除了多線程工作外幾乎是相同的,包括所有控制參數、收集演算法、stop the world,對象分配規則,回收策略等都是一樣的。運行流程如下圖:

  雖然與Serial收集器相比僅僅多了多線程特性外,沒有其它的創新之處,但是它卻是許多Server模式下的虛擬機新生代收集器的首選,原因在於目前為止只有Serial和ParNew兩個新生代收集器能夠與性能優異的CMS配合使用。關於CMS介紹將在下文展開描述。

 

  Parallel Scavenge 收集器

  Parallel Scavenge也是一款使用複製演算法的新生代收集器。該收集器與其它收集器不同的是,它關註的目標是達到一個可控制的吞吐量,而CMS等收集器的關註點則是儘可能地減少用戶線程地停頓時間,提高用戶體驗。Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量,分別是控制最大垃圾收集停頓時間的-XX:MaxGCPauseMillis參數以及直接設置吞吐量大小的-XX:GCTimeRatio參數。也因此,Parallel Scavenge 被成為“吞吐量優先”收集器。

  停頓時間越短就越適合與用戶交互較多地程式,這樣用戶體驗才更好。而高吞吐量則可以讓出更多的cpu資源給用戶線程,讓程式更快的完成運算任務,更適合後臺運算較多而不需要與用戶交互的程式。

  自適應調節策略是Parallel Scavenge收集器的特點,也是與ParNew收集器的區別。Parallel Scavenge通過打開-XX:+UseAdaptiveSizePolicy的設置,就不需要手動地調節新生代(-Xmn)大小,Eden和Survivor區的比例(-XX:SurvivorRatio)、晉升老年代對象年齡(-XX:PretenureSizeThreshold)等細節參數,而是根據當前系統運行情況來確定這些參數,從而提高程式地吞吐量和縮短停頓時間,這一過程稱之為GC自適應的調節策略(GC Ergonomics)。

  另外值得註意的一點是,Parallel Scavenge無法已CMS配合使用,如果新生代選擇了Parallel Scavenge收集器,那麼老年代的收集器只能選用Serial Old或者Parallel Old來配置使用。

 

  Serial Old收集器

  Serial Old是Serial收集器的老年代版本,也是單線程工作的,使用的是“標記-整理”演算法。

  該收集器主要用於Client模式下的虛擬機使用,如果在Server模式下可以與Parallel Scavenge收集器配合使用;作為CMS收集器的後備預案,在併發收集發生Concurrent Mode Failure時使用。運行流程如下:

 

  Parallel Old收集器

  Parallel Old是Parallel Scavenge的老年代版本,也是一個並行收集器,使用“標記-整理”演算法。該收集器在jdk1.6後對外提供使用,Parallel Scavenge 和  Parallel Old配合使用的話,更加適合應用於高吞吐量和cpu敏感資源的場合。下麵是這兩個收集器配合使用的運行流程:

 

  CMS收集器

  CMS(Concurrent Mark Sweep)是一個併發收集器,使用了“標記-清除”演算法來實現的。該收集器最求的更短的停頓時間,從而提升用戶體驗,因此也非常符合使用在網站、B/S系統的服務端的應用。

  CMS收集器的工作流程大概可以分為以下4個步驟:

  • 初始標記:這個階段僅僅標記能夠和gc roots直接關聯的對象,速度很快,但是需要“stop the world”。
  • 併發標記:這個階段開始進行gc roots tracing標記,與用戶線程一起執行的,消耗時間很多。
  • 重新標記:這個階段是要是修正在併發標記期間由於用戶線程也在運行而產生標記變動的那部分對象的標記,比較耗費的時間比初始標記階段要長,但是遠比併發標記階段要短,這個過程也是需要“stop the world”的。
  • 併發清除:對無用對象進行回收操作。這個過程與用戶線程並行執行。

  由於標記和清除階段可以和用戶線程一起工作,因此幾乎可以把CMS收集器的工作是併發的:

  CMS是一款優秀的收集器,它的主要優點是低停頓,併發收集,因此也被成為併發低停頓收集器(Concurrent Low Pause Collector)。

  當然,CMS收集器也有一定的缺點,主要包括一下幾點:

  • CMS收集器使用“標記-清除”演算法實現,因此不可避免地有記憶體碎片地問題。當記憶體碎片過多時,在分配大對象地過程中即使有足夠的空間,但是找不到足夠地連續的空間來放該對象,那麼就有可能觸發一次full gc。
  • 無法處理浮動垃圾(Floating Garbage) 可能出現“Concurrent Mode Failure”失敗而導致另一次Full GC的產生。這是因為在標記的過程中用戶線程也在運行著,那麼在這一過程中出現的垃圾無法立即回收,而是等下一次gc才能清理,我這部分的垃圾就叫做“浮動垃圾”。也是由於在垃圾收集階段用戶線程還需要運行,那也就還需要預留有足夠的記憶體空間給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預留一部分空間提供併發收集時的程式運作使用。
  • 對cpu資源非常敏感。其實,只要是面對併發的情況下都會有這個問題,在併發階段雖然不會中斷用戶線程,但是因為占用了部分用戶的資源而導致程式變慢,總吞吐量降低。CMS搜集器預設的線程數 = (cpu核數 + 3) / 4,當cpu數量大於4時,垃圾回收線程數不少於25%,隨著線程數的增加而下降,當cpu數量小於4時對線程的執行效率有顯著的影響。

  運行示意圖如下:

  

  G1收集器

  G1(Garbage-First)是一款面向服務端應用的垃圾收集器,JDK 7 Update4 後開始進入商用。HotSpot開發團隊賦予它的使命是未來可以替換掉JDK 1.5中發佈的CMS收集器。之前提供的收集器都是僅作用於新生代或者是老年代,但是G1收集器可以作用於新生代和老年代,因為使用G1收集器是java heap的記憶體結構有很大的不同,它將整個Java堆劃分為多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但是他們已經沒有了物理上的隔閡了,它們都是region的一部分的集合。

  G1(Garbage-First)收集器是當今收集器技術發展的最前沿成果之一,與其他收集器相比,G1收集器具有以下特征:

  • 並行與併發: G1能充分利用多CPU,多核環境下的硬體優勢,使用多個CPU來縮短Stop-The-World停頓時間,部分其他收集器原本需要停頓java線程執行的GC動作,G1收集器仍然可以通過併發的方式讓java程式繼續執行。
  • 分代收集: 與其他收集器一樣,分代概念在G1中仍然得以保留。雖然G1可以不需要其他收集器配合能夠獨立管理整個堆,但它能夠採用不用的方式去處理新創的對象和已經存活了一段世紀那、熬過多次GC的舊對象以獲得更好的收集效果。
  • 空間整合: 與CMS的“標記-清除”演算法不同,G1整體來看採用了“標記-整理”演算法實現的收集器,從局部(兩個Region之間)上來看是基於“複製”演算法實現的。無論使用哪一種方法,都意味著G1運作期間不會產生記憶體空間碎片的問題,收集後能提供規整的可用空間。這種特性有利於程式長時間運行,分配大對象是不會因為無法得到連續記憶體空間而提前處罰一次GC。
  • 可預測的停頓: 這是G1相對於CMS的另一大優勢,降低停頓時間是G1和CMS共同的關註點,但G1除了最求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒,這幾乎是java(RTSJ)的垃圾收集器的特征了。

  G1收集器之所以能夠建立可預測的停頓時間模型,因為他能夠有計劃地避免在整個Java堆中進行全區域的垃圾收集。G1跟蹤各個Region裡面的垃圾堆積的價值大小(回收所獲得的空間大小以及回收所需要的經驗值),在後臺維護一個優先表,每次根據允許的收集時間,優先回收價值最大的Region。這種使用Region劃分記憶體空間以及有優先順序的區域回收方式,保證了G1收集器在有限的時間內可以獲取儘可能高的收集效率。

  在G1收集器中,Region之間的對象引用以及其他收集器中的新生代和老年代之間的對象引用,虛擬機都是使用Remembered Set來避免全堆掃描的。G1中每個Region都有一個與之對應的Remembered Set,虛擬機發現程式在對Reference類型的數據進行寫操作時,會產生一個Write Barrier暫時中斷寫操作,檢查Reference引用的對象是否處於不同的Region中,如果是,便通過CardTable把相關引用信息記錄到被引用對象所屬的Region中的Remembered Set之中。當進行記憶體回收時,在GC根節點的枚舉範圍中加入Remembered Set即可保證不對全堆掃描,也不會有遺漏。

  如果不計算維護Remembered Set的操作,G1收集器的運作大致分為以下幾個步驟:

  1. 初始標記(Initial Marking): 這階段僅僅只是標記GC Roots能直接關聯到的對象並修改TAMS(Next Top at Mark Start)的值,讓下一階段用戶程式併發運行時,能在正確的可用的Region中創建新對象,這階段需要停頓線程,但是耗時很短。
  2. 併發標記(Concurrent Marking): 從GC Roots 開始對堆的對象進行可達性分析,找出存活的對象,這階段耗時長,但是可以與用戶程式併發執行。
  3. 最終標記(Final Marking): 為了修正在併發標記期間因為用戶程式繼續運行而導致標記產生變動的那一部分標記記錄,虛擬機將這段時間對象變化記錄記錄線上程Remembered Set Logs裡面。
  4. 篩選回收(Live Data Counting and Evacuation): 首先對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃,這一階段是可以與用戶程式一起併發執行的,但是因為只回收部分Region,時間是用戶可控的,而且停頓用戶線程將大幅度提高收集效率。

  執行流程如下圖:

  

  總結

  

收集器串列、並行or併發新生代/老年代演算法目標適用場景
Serial 串列 新生代 複製演算法 響應速度優先 單CPU環境下的Client模式
Serial Old 串列 老年代 標記-整理 響應速度優先 單CPU環境下的Client模式、CMS的後備預案
ParNew 並行 新生代 複製演算法 響應速度優先 多CPU環境時在Server模式下與CMS配合
Parallel Scavenge 並行 新生代 複製演算法 吞吐量優先 在後臺運算而不需要太多交互的任務
Parallel Old 並行 老年代 標記-整理 吞吐量優先 在後臺運算而不需要太多交互的任務
CMS 併發 老年代 標記-清除 響應速度優先 集中在互聯網站或B/S系統服務端上的Java應用
G1 併發 both 標記-整理+複製演算法 響應速度優先 面向服務端應用,將來替換CMS

 

  參考資料: 《深入理解Java虛擬機-JVM高級特性與最佳實踐》 -周志明

  喜歡我寫的博客的同學可以關註訂閱號【Java解憂雜貨鋪】,裡面不定期發佈一些技術幹活,也可以免費獲取大量最新最流行的技術教學視頻

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 將👇代碼粘貼到自己Django項目的配置文件中 在自己的項目中創建 log 文件夾 ...
  • Java編程語言是一種可以撰寫跨平臺應用軟體的面向對象的程式設計語言。在全球雲計算和移動互聯網的產業環境下,Java更具備了顯著優勢和廣闊前景。 ...
  • 作者:CHAITANYA SINGH 來源:https://www.koofun.com/pro/kfpostsdetail?kfpostsid=17 運算符是表示動作的字元,例如+是表示加法的算術運算符。 Java中的運算符類型有七大類:基本算術運算符、賦值運算符、自增運算符和自減運算符、邏輯運算 ...
  • [TOC] 一、自定義通用對話框 仿照windows使用Qt實現了一個自定義通用對話框,支持左側節點擴展,右側也支持註冊自定義視窗,例如文件預覽等等,可以和中間視圖列表進行通信 支持打開、保存、另存為3種模式 支持5種預覽模式 支持前進後退操作 之前當前目錄搜索 新增文件、刪除文件 文件重命名 文件 ...
  • import java.util.Scanner; /** * 斐波那契數列的和 * @author fish shadow * 輸入兩個正整數k1,k2(保證k1<=k2)。輸出斐波那契數列的第k1項到第k2項的和。 * 測試案例: * 輸入: * 3 6 *輸出: *18 *(解釋2+3+5+8 ...
  • 今天的面試題以基礎為主,去面試Python工程師,這幾個基礎問題不能答錯 第1題:列表和元組有什麼不同? 列表和元組是Python中最常用的兩種數據結構,字典是第三種。 相同點: 都是序列 都可以存儲任何數據類型 可以通過索引訪問 語法差異 使用 創建列表,而 創建元組。 是否可變 列表是 ,而元組 ...
  • python視頻 神經網路 Tensorflow 模塊 視頻教程 (帶源碼) python視頻 神經網路 Tensorflow 模塊 視頻教程 (帶源碼) python視頻 神經網路 Tensorflow 模塊 視頻教程 (帶源碼) 所屬網站分類: 資源下載 > python視頻教程 作者:smil ...
  • 參考博文:https://www.cnblogs.com/weven/p/7252917.html 以下是我按照參考博文進行實際操作的過程,此篇只作記錄。 一、下載 1、打開官網https://www.python.org/downloads/windows/,點擊Python 2版本鏈接 2、點擊 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...