Java虛擬機四:垃圾回收演算法與垃圾收集器

来源:https://www.cnblogs.com/fengweiweicoder/archive/2019/04/30/10787454.html
-Advertisement-
Play Games

在Java運行時的幾個數據區域中,程式計數器,虛擬機棧,本地方法棧3個區域隨著線程而生,隨線程而滅,因此這幾個區域的記憶體分配和回收具有確定性,不需要過多考慮垃圾回收問題,因為方法結束或者線程結束時,記憶體就回收了。但是方法區和堆區不一樣,一個介面或者實現類所需要的記憶體可能不一樣,一個方法的多個分支需要 ...


  在Java運行時的幾個數據區域中,程式計數器,虛擬機棧,本地方法棧3個區域隨著線程而生,隨線程而滅,因此這幾個區域的記憶體分配和回收具有確定性,不需要過多考慮垃圾回收問題,因為方法結束或者線程結束時,記憶體就回收了。但是方法區和堆區不一樣,一個介面或者實現類所需要的記憶體可能不一樣,一個方法的多個分支需要的記憶體也可能不一樣,只有程式運行時才能知道創建哪些對象,這部分記憶體的分配和回收是動態的。

  在進行垃圾回收時候,首先需要判斷哪些對象需要回收,這就涉及到回收演算法的問題。

一、垃圾回收演算法

1.標記-清除演算法

  標記-清除演算法是一種最基礎的垃圾收集演算法,分為“標記”和“清除”兩步。“標記”階段標記所有需要進行垃圾回收的對象,標記完成後統一回收被標記的對象。這種演算法的不足點在於:

  (1)效率問題,標記和清除兩個過程效率都不高;

  (2)空間問題,標記清除後會產生大量不連續碎片,後續如果需要為較大對象分配空間,則又需觸發垃圾回收。

2.複製演算法

  為瞭解決標記-清除演算法的效率問題,出現了複製演算法。這種演算法把記憶體按照容量劃分為大小相同的兩塊,每次只是用其中一塊,當這塊記憶體用完了,就把還存活的對象複製到另外一塊中,並將這塊的記憶體清理掉,然後使用另外一塊,當另外一塊記憶體用完了,再把存活的對象複製到這塊中,並清理另外一塊記憶體,依次類推。

  複製演算法主要用於新生代的回收,在HotSpot虛擬機中,新生代記憶體劃分為一塊較大的Eden空間,和兩塊較小的Survivor空間,每次使用Eden空間和其中一塊Survivor空間。當進行垃圾回收時,會把Eden空間和Survivor空間中存活的對象一次性複製到另外一塊Survivor空間上,最後清理掉Eden空間和剛纔使用過的Survivor空間。HotSpot虛擬機中,預設情況下Eden空間和Survivor空間的大小比例是8:1,即Eden空間占整個新生代的80%,每次新生代中使用的空間為80%+10%=90%,閑置空間10%。

3.標記-整理演算法

  複製演算法適用於那種對象存活率較低的場景,在對象存活率較高時,使用複製收集演算法意味著需要進行大量複製,會使效率降低,同時複製大量存活對象到另外一塊記憶體,意味著需要有足夠大的記憶體來保存這些對象,這勢必會降低記憶體使用率。根據老年代的特點,有人提出標記-整理演算法,和標記-清除演算法不同的是,標記整理演算法將存活的對象向一端移動,然後直接清理掉端邊界之外的記憶體。

4.分代收集演算法

  目前商業虛擬機中都使用分代收集演算法。一般將Java堆分為新生代和老年代,新生代進行垃圾收集發現有大量對象死去,只有少量對象存活,那麼就使用複製演算法。老年代中對象存活率較高,使用標記-清除演算法或者標記-整理演算法。

二、垃圾收集器

   垃圾收集演算法提供了記憶體回收的方法論,垃圾收集器是記憶體回收的方法論。每個廠商對垃圾收集器的實現不一樣,這裡主要討論Jdk1.7 Update 14之後的HotSpot虛擬機。這個虛擬機中包含的垃圾收集器有如下7種:

                                     

    以上收集器之間如果有連線,則表明可以搭配使用,虛擬機所處區域,表示他是新生代收集器還是老年代收集器。

1.Serial收集器

  Serial收集器是一種最基本的單線程收集器,這種收集器工作時,必須停止其他所有工作線程,優點在於簡單高效,但體驗很不友好,目前主要應用場合是:虛擬機運行在Client模式下的預設新生代收集。器。

2.ParNew收集器

  parNew收集器是Serial收集器的多線程版本,常用參數設置:

   -XX:+UseConcMarkSweepGC :設置ParNew為預設的新生代收集器;

   -XX:+UseParNewGC :指定使用ParNew為年輕代收集器,強制指定;

   -XX:ParallelGCThreads=n :設置收集器的線程數為n。

3.Parallel Scavenge收集器

  Parallel Scavenge收集器是一個使用複製演算法的新生代收集器,這種收集器的主要目標是達到一個可控制的吞吐量(Throughput,CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間))。由於與吞吐量關係密切,故而Parallel Scavenge收集器也稱為“吞吐量優先”收集器。常用參數設置:

   -XX:MaxGCPauseMillis=n :設置年輕代垃圾收集的最長時間;

   -XX:GCTimeRatio=n :設置垃圾收集總可用時長的比例,和吞吐量直接相關;

   -XX:+UseAdaptiveSizePolicy 自適應大小開關,配置該選項之後,每次GC後會重新計算 Eden、From 和 To 區的大小,計算依據是 GC 過程中統計的 GC 時間、吞吐量、記憶體占用量,因此設置此參數之後就不需要再設置 -XX:SurvivorRatio 、 -XX:PretenureSizeThreshold 等參數了。

4.Serial Old收集器

  Serial收集器的老年版本,也是一個單線程收集器,使用的是“標記-整理”演算法,這種收集器的主要意義也是給Client模式下的虛擬機使用。

5.Parallel Old收集器

  Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”演算法。在註重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge收集器和Parallel Old收集器的組合。

6.CMS收集器

  CMS收集器是一種以獲取最短回收停頓時間為目標的收集器。它基於“標記-清除”演算法實現,運作過程相對於其他幾種收集器更複雜一些。分為以下四個過程:

  (1)初始標記(CMS initial mark):標記一下CG Roots能關聯到的對象;

  (2)併發標記(CMS concurrent mark):進行CG Roots Tracing的過程;

  (3)重新標記(CMS remark):修正併發標記期間因用戶程式繼續運作而導致標記產生變動的那一部分對象的標記記錄。

  (4)併發清理(CMS concurrent sweep)

  CMS 收集器的優點在於併發收集,低停頓。其缺點在於以下三點:

  (1)CMS收集器對CPU很敏感,CMS預設回收線程是(CPU數量+3)/4,當CPU在4個以上時,併發收集時垃圾收集線程不少於25%的CPU資源,並隨著CPU數量增加而下降。但是當CPU不足4個時,CMS對用戶程式的影響就會變得很大。

  (2)CMS收集器無法處理浮動垃圾。由於CMS收集器併發清理階段用戶線程還在運行著,伴隨著程式運行就會有垃圾產生,這部分垃圾在標記過後,CMS收集器無法在當次收集中清理這些垃圾。

  (3)由於CMS收集器是一種基於“標記-清除”演算法的收集器,這種演算法實現的收集器在收集結束後會有大量不連續碎片產生。碎片過多時會給大對象分配帶來很大麻煩,往往老年代還有很大空間剩餘,但是無法找到連續空間分配當前對象,因而不得不提前觸發Full GC。

7.G1收集器

  G1收集器是一款面向服務端應用的垃圾收集器,與其他收集器相比,G1收集器具有如下優點:

  (1)併發與並行:G1能充分利用多CPU,多核硬體優勢,使用多個CPU來減少停頓時間;

  (2)分代收集:G1不需要其他收集器配合就能獨立管理整個堆的垃圾收集,且它能採用不同方式去處理新建對象和已經存活了一段時間,熬過多次GC的舊對象以獲得更好的收集效果。

  (3)空間整合:使用G1收集器不會產生記憶體碎片,收集後能提供規整的可用記憶體。這種特性有利於程式長時間運行,分配大對象時候不會因為無法找到連續記憶體空間而提前觸發下一次GC.

  (4)可預測的停頓:G1除了追求低停頓,還能建立可預測的停頓時間模型,能讓使用著指定在長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不超過N毫秒。

 

三、垃圾收集參數總結

參數描述
UseSerialGC

虛擬機運行在Client模式下的預設值,打開此開關後,使用Serial+Serial Old的收集器組合進行記憶體回收

UseParNewGC 打開此開關後,使用ParNew + Serial Old 的收集器組合進行記憶體回收
UseConcMarkSweepGC 打開此開關後,使用ParNew + CMS + Serial Old 的收集器組合進行記憶體回收。Serial Old 收集器將作為CMS收集器出現Concurrent Mode Failure失敗後的後備收集器使用
UseParallelGC 虛擬機運行在Server 模式下的預設值,打開此開關後,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器組合進行記憶體回收
UseParallelOldGC 打開此開關後,使用Parallel Scavenge + Parallel Old 的收集器組合進行記憶體回收
SurvivorRatio 新生代中Eden 區域與Survivor 區域的容量比值,預設為8,代表Eden :Survivor=8∶1
PretenureSizeThreshold 直接晉升到老年代的對象大小,設置這個參數後,大於這個參數的對象將直接在老年代分配
MaxTenuringThreshold 晉升到老年代的對象年齡。每個對象在堅持過一次Minor GC 之後,年齡就加1,當超過這個參數值時就進入老年代
UseAdaptiveSizePolicy 動態調整Java 堆中各個區域的大小以及進入老年代的年齡
HandlePromotionFailure 是否允許分配擔保失敗,即老年代的剩餘空間不足以應付新生代的整個Eden 和Survivor 區的所有對象都存活的極端情況
ParallelGCThreads 設置並行GC 時進行記憶體回收的線程數
GCTimeRatio GC 時間占總時間的比率,預設值為99,即允許1% 的GC 時間。僅在使用Parallel Scavenge 收集器時生效
MaxGCPauseMillis 設置GC 的最大停頓時間。僅在使用Parallel Scavenge 收集器時生效
CMSInitiatingOccupancyFraction 設置CMS 收集器在老年代空間被使用多少後觸發垃圾收集。預設值為68%,僅在使用CMS 收集器時生效
UseCMSCompactAtFullCollection 設置CMS 收集器在完成垃圾收集後是否要進行一次記憶體碎片整理。僅在使用CMS 收集器時生效
CMSFullGCsBeforeCompaction 設置CMS 收集器在進行若幹次垃圾收集後再啟動一次記憶體碎片整理,僅在使用CMS 收集器時生效

 

 

參考資料:《深入理解Java虛擬機 JVM高級特性與最佳實踐 第2版》


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

-Advertisement-
Play Games
更多相關文章
  • 小伙伴們知道《勵學篇》嗎?他是宋真宗趙恆寫的。如果現在的你是正在教室里備戰高考的學子,那十有八九會知道詩句的內容。但是對於已經許久沒讀過古詩句的你,可能我這麼說,你未必會知道他是誰?這篇文章是什麼?但是看完下麵的詩句,你肯定會恍然大悟,噢,原來是這些呀。 那是什麼詩句呢?哈哈,請看「 富家不用買良田 ...
  • [TOC] 面向過程編程是解決問題的一種思想,相當於武林門派,武林門派之間沒有好壞之分,因此它與我們之後學習的面向對象編程其實沒有好壞之分。 面向過程編程,核心是編程二字,過程指的是解決問題的步驟,即先乾什麼、後乾什麼、再乾什麼、然後乾什麼…… 基於該思想編寫程式就好比在設計一條流水線,面向對稱編程 ...
  • 所屬網站分類: python基礎 > 異常處理 作者:浮沉 鏈接:http://www.pythonheidong.com/blog/article/71/ 來源:python黑洞網,專註python資源,python教程,python技術! 我知道你能做到: try: # do something ...
  • PHP提示Notice: Undefined variable,意思是:你的程式總有有未定義的變數 為什麼在其他地方好好的程式,換個環境報這個Notice,因為php.ini提醒級別設置的問題 場景複原: 舉例,打開php.ini配置文件,搜索error_reporting,讓這個配置的值如下圖 重 ...
  • 整數反轉 題目描述 給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。 示例 1: 輸入: 123 輸出: 321 示例 2: 輸入: -123 輸出: -321 示例 3: 輸入: 120 輸出: 21 註意: 假設我們的環境只能存儲得下 32 位的有符號整數,則其數值範圍為 ...
  • 使用keytool工具產生帶根CA和二級CA的用戶證書 1 生成根CA 1.1 生成根CA證書 根CA實際是一張自簽CA,自簽CA的使用者和頒發者都是它自己。使用下麵的命令生成根證書,如果沒有指定 則會使用預設在用戶Home目錄下的 秘鑰庫(如果沒有則會創建),輸入秘鑰庫的密碼,填寫根證書的信息,最 ...
  • Python基礎之變數進階,包括了 變數的引用,可變類型和不可變類型,哈希;其中,變數的引用 包括 函數引用的概念,函數引用理解,函數傳參與引用的關係,函數返回值與引用;可變類型和不可變類型 包括 可變類型修改和重賦值對引用的影響;哈希 僅包含 哈希演算法 等 ...
  • 在上一章的指南中,我們寫了一個命名隊列:生產者往該命名隊列發送消息、消費從從該命名隊列中消費消息。在本章中,我們將創建一個工作隊列,用於在多個工作者之間分配耗時的任務。工作隊列(即任務隊列)的主要思想是避免立即執行那些需要等他們執行完成的資源密集型任務。相反,我們將任務安排在稍後完成。我們將任務封裝 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...