JVM垃圾回收器

垃圾回收算法

標記-復制

缺點:內存利用率低,有一塊區域無法使用。

標記-清除

缺點:

1. 效率問題 (如果需要標記的對象太多,效率不高)

2. 空間問題(標記清除后會產生大量不連續的碎片)

標記-整理

分代收集

????????根據對象存活周期的不同將內存分為幾塊。一般將java堆分為新生代和老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。

????????當前虛擬機的垃圾收集都采用分代收集算法。

垃圾回收器

新生代和老年代使用的垃圾收集器的組合:

Serial收集器

(-XX:+UseSerialGC -XX:+UseSerialOldGC)

特點:

  • 新生代采用復制算法,老年代采用標記-整理算法。
  • "Stop The World"
  • 簡單而高效(與其他收集器的單線程相比)

Serial Old收集器是Serial收集器的老年代版本,使用場景:

  • 在JDK1.5以及以前的版本中與Parallel Scavenge收集器搭配使用
  • 另一種用途是作為CMS收集器的后備方案

Parallel Scavenge收集器

(-XX:+UseParallelGC(年輕代),-XX:+UseParallelOldGC(老年代))

  • 新生代采用復制算法,老年代采用標記-整理算法。Serial收集器的多線程版本。
  • "Stop The World"
  • 默認的收集線程數跟cpu核數相同,當然也可以用參數(-XX:ParallelGCThreads)指定收集線程數,但是一般不推薦修改

與其他垃圾收集器比較:

  • Parallel收集器關注點是吞吐量(用戶線程的高效率的利用CPU)(縮短垃圾回收時間)。
  • CMS等垃圾收集器的關注點更多的是用戶線程的停頓時間(提高用戶體驗)(stop the world時間)

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,,使用場景:

在注重吞吐量以及CPU資源的場合,都可以優先考慮 Parallel Scavenge收集器和Parallel Old收集器(JDK8默認的新生代和老年代收集器)。

ParNew收集器

(-XX:+UseParNewGC)

  • 新生代采用復制算法
  • 跟Parallel收集器很類似, 但是它只能用于新生代,和CMS收集器配合使用(Parallel收集器不能和CMS配合使用)

CMS收集器

(-XX:+UseConcMarkSweepGC(old))

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。它非常符合在注重用戶體驗的應用上使用,它是HotSpot虛擬機第一款真正意義上的并發收集器,它第一次實現了讓垃圾收集線程與用戶線程(基本上)同時工作。基于“標記-清除”算法實現。

標記過程:

1. 初始標記:

stop the world,?并記錄下gc roots直接能引用的對象(根對象),速度很快。

2. 并發標記:

從GC Roots的直接關聯對象開始遍歷整個對象圖的過程, 這個過程耗時較長但是不需要停頓用戶線程。因為用戶程序繼續運行,可能會有導致已經標記過的對象狀態發生改變。出現多標注或者漏標注。

多標注會出現浮動垃圾,可以接受,可以在下一次的gc時回收掉; 漏標注很嚴重,未標注的會被垃圾回收,JVM不能回收還被引用著的對象。

3. 重新標記:

stop the world,修正并發標記期間因為用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄(主要是處理漏標問題),這個階段的停頓時間一般會比初始標記階段的時間稍長,遠遠比并發標記階段時間短。主要用到三色標記里的 增量更新算法。

4. 并發清理:

開啟用戶線程,同時GC線程開始對未標記的區域做清掃。這個階段如果有新增對象會被標記為黑色不做任何處理。

5. 并發重置:

重置本次GC過程中的標記數據

CMS優點:并發收集、低停頓

CMS缺點:

  1. 對CPU資源敏感(會和服務(用戶線程)搶資源);
  2. 無法處理浮動垃圾(在并發標記和并發清理階段又產生垃圾,這種浮動垃圾只能等到下一次gc再清理了);
  3. 標記-清除算法會導致收集結束時會有大量空間碎片產生,當然通過參數-XX:+UseCMSCompactAtFullCollection可以讓jvm在執行完標記清除后再做整理
  4. 執行過程中的不確定性。會存在上一次垃圾回收還沒執行完,然后垃圾回收又被觸發的情況,特別是在并發標記和并發清理階段會出現。因為是 一邊回收,系統一邊運行,也許沒回收完就再次觸發full gc,也就是"concurrent mode failure",此時會進入stop the?world,用serial old垃圾收集器來回收

CMS的相關核心參數

1. -XX:+UseConcMarkSweepGC:啟用cms

2. -XX:ConcGCThreads:并發的GC線程數

3. -XX:+UseCMSCompactAtFullCollection:FullGC之后做壓縮整理(減少碎片)

4. -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后壓縮一次,默認是0,代表每次FullGC后都會壓縮一次

5. -XX:CMSInitiatingOccupancyFraction: 當老年代使用達到該比例時會觸發FullGC(默認是92,這是百分比)

6. -XX:+UseCMSInitiatingOccupancyOnly:只使用設定的回收閾值(-XX:CMSInitiatingOccupancyFraction設定的值),如果不指定,JVM僅在第一次使用設定值,后續則會自動調整

7. -XX:+CMSScavengeBeforeRemark:在CMS GC前啟動一次minor gc,降低CMS GC標記階段(也會對年輕代一起做標記,如果在minor gc就干掉了很多對垃圾對象,標記階段就會減少一些標記時間)時的開銷,一般CMS的GC耗時 80%都在標記階段

8. -XX:+CMSParallellnitialMarkEnabled:表示在初始標記的時候多線程執行,縮短STW

9. -XX:+CMSParallelRemarkEnabled:在重新標記的時候多線程執行,縮短STW;

JVM參數優化

億級流量電商系統如何優化JVM參數設置(ParNew+CMS)

對于8G內存,我們一般是分配4G內存給JVM,正常的JVM參數配置如下:

-Xms : 堆初始內存大小

-Xmx:最大堆內存大小

-Xmn :?新生代初始和最大值

-Xss :?每個線程的棧大小

-XX:MetaspaceSize :?初始元空間大小

-XX:MaxMetaspaceSize :?最大元空間大小

-XX:SurvivorRatio :?設置 Eden 區與 Survivor 區的比例

-Xms3072M -Xmx3072M

-Xss1M

-XX:MetaspaceSize=256M

-XX:MaxMetaspaceSize=256M

-XX:SurvivorRatio=8

這樣的配置, young?=1G??(eden = 819M?, survivor = 102M)? ?old = 2048M= 2G

每秒60M垃圾, 大約819?/ 60 = 14s占滿eden, 觸發minor gc

優化?: 修改JVM參數

-Xms3072M -Xmx3072M -Xmn2048M

-Xss1M

-XX:MetaspaceSize=256M

-XX:MaxMetaspaceSize=256M

-XX:SurvivorRatio=8

這樣的配置, young?=2G??(eden =1638M , survivor = 204M)? ?old = 1G

每秒60M垃圾, 大約1638?/ 60 = 27s占滿eden, 觸發minor gc

JVM優化手段:

1. 無非就是讓短期存活的對象盡量都留在survivor里,不要進入老年代,這樣在minor gc的時候這些對象都會被回收,不會進到老年代從而導致full gc。

2.?對象年齡應該為多少才移動到老年代比較合適:大多數對象一般在幾秒內就會變為垃圾,完全可以將默認的15歲改小一點,比如改為5, 從而減少survivor區的占用。

// 修改移動到老年代判斷的標準

-XX:MaxTenuringThreshold=5

-XX:PretenureSizeThreshold=1M? ??直接晉升老年代的對象大小閾值

3. 如果JVM內存超過4G, 就不適合JDK8默認的垃圾收集器(Parallel Scavenge收集器和Parallel Old收集器), 考慮使用ParNew + CMS收集器。

參數配置:

-Xms3072M -Xmx3072M -Xmn2048M

-Xss1M

-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M

-XX:SurvivorRatio=8

-XX:MaxTenuringThreshold=5

-XX:PretenureSizeThreshold=1M

-XX:+UseParNewGC

-XX:+UseConcMarkSweepGC // 啟用CMS

-XX:CMSInitiatingOccupancyFraction=92

-XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction=3

垃圾收集底層算法實現

三色標記

三色標記算法是把Gc roots可達性分析遍歷對象過程中遇到的對象, 按照“是否訪問過”這個條件標記成以下三種顏色:

1. 黑色: 表示對象已經被垃圾收集器訪問過, 且這個對象的所有引用都已經掃描過。黑色表示這個對象是安全存活的。

所有引用: A->B->C, A->D, A的所有引用指的是B和D, 并不包括C.

2. 灰色: 表示對象已經被垃圾收集器訪問過, 但這個對象上至少存在一個引用還沒有被掃描過。

3. 白色: 表示對象尚未被垃圾收集器訪問過。 顯然在可達性分析剛剛開始的階段, 所有的對象都是白色的, 若在分析結束的階段,仍然是白色的對象, 即代表不可達。

浮動垃圾(多標了存活對象)

????????并發標記過程中,被標記了的對象有被用戶線程銷毀,這部分本應該回收但是沒有回收到的內存,被稱之為“浮動垃圾”。浮動垃圾并不會影響垃圾回收的正確性,只是需要等到下一輪垃圾回收中才被清除。

????????另外,針對并發標記(還有并發清理)開始后產生的新對象,通常的做法是直接全部當成黑色。

讀寫屏障(漏標了存活對象)

????????漏標會導致被引用的對象被當成垃圾誤刪除,這是嚴重bug,必須解決,有兩種解決方案: 增量更新(Incremental Update) 和原始快照(Snapshot At The Beginning,SATB)。

漏標分析:

在并發標記的過程中,如果出現b.d = null, a.d = D, 就會變成右邊的引用鏈。

按照三色標記算法,A是黑色,表示A已經掃描完成不會再被掃描,此時D并不是垃圾,但是GC root可達性分析并沒有標記出D,D是會被回收的,這就是漏標,這是不可接受的。

1. 增量更新(Incremental Update

? ? ? ? 關注增量的引用。當黑色對象插入新的指向白色對象的引用關系時, 就將這個新插入的引用記錄用一個集合記錄下來, 等并發掃描結束之后, 再將這些記錄過的引用關系中的黑色對象為根, 重新掃描一次(重新標記階段)。 這可以簡化理解為, 黑色對象一旦新插入了指向白色對象的引用之后,它就變回灰色對象了

? ? ? ? 通過寫屏障實現。

2. 原始快照(Snapshot At The Beginning,SATB)

? ? ? ? 關注刪除引用。當灰色對象要刪除指向白色對象的引用關系時, 就將這個要刪除的引用 記錄下來(b.d = null , 那么將d記錄到集合中), 在并發掃描結束之后, 再將這些記錄過的引用關系中的灰色對象為根, 重新掃描一次,這樣就能掃描到白色的對象,將白色對象直接標記為黑色(目的就是讓這種對象在本輪gc清理中能存活下來,待下一輪gc的時候重新掃描,這個對象也有可能是浮動垃圾)

? ? ? ? 通過寫屏障實現。

寫屏障

所謂的寫屏障,其實就是指在賦值操作前后,加入一些處理(可以參考AOP的概念):

void oop_field_store(oop* field, oop new_value) {pre_write_barrier(field); // 寫屏障-寫前操作*field = new_value;post_write_barrier(field, value); // 寫屏障-寫后操作
}

讀屏障

oop oop_field_load(oop* field) {pre_load_barrier(field); // 讀屏障-讀取前操作return *field;
}

對于讀寫屏障,以Java HotSpot VM為例,其并發標記時對漏標的處理方案如下:

CMS:寫屏障 + 增量更新

G1Shenandoah:寫屏障 + SATB

ZGC:讀屏障

為什么G1用SATB?CMS用增量更新?

簡單理解:SATB相對增量更新效率會高(當然SATB可能造成更多的浮動垃圾),因為不需要在重新標記階段再次深度掃描被刪除引用對象,而CMS對增量引用的根對象會做深度掃描,G1因為很多對象都位于不同的region,CMS就一塊老年代區域,重新深度掃描對象的話G1的代價會比CMS高,所以G1選擇SATB不深度掃描對象,只是簡單標記,等到下一輪GC再深度掃描。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93287.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93287.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93287.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

科研工具的一些注意事項

Origin Origin導入數據之后,可以考慮 [刪除數據連接器…] 導入數據之后,刪除數據連接,這樣當原來的文件移動之后,就不影響origin文件里面的數據。不然就會出現空白數據:當然,沒有數據了也可以加載出來&…

美國服務器環境下Windows容器工作負載智能彈性伸縮

在北美數據中心加速數字化轉型的今天,企業客戶日益重視Windows容器工作負載的智能化管理。本文將深入探討基于Azure Stack HCI(混合云基礎設施)的彈性伸縮方案如何突破傳統資源調度瓶頸,通過分析指標收集、策略配置、混合云聯動三…

歐姆龍CP系列以太網通訊實現上位機與觸摸屏監控

一、行業痛點在現代工業生產中,自動化生產線的控制系統的高效性與智能化程度對生產效率和產品質量有著至關重要的影響。然而,許多傳統工業生產線中使用的歐姆龍CP系列系列PLC以太網模塊,由于自身設計原因,并未配備以太網接口&…

【大語言模型 00】導讀

【大語言模型00】導讀:你的LLM全棧工程師進階之路關鍵詞:大語言模型、LLM、Transformer、深度學習、AI工程化、全棧開發、技術路線圖摘要:這是一份完整的大語言模型學習指南,涵蓋從數學基礎到商業落地的200篇深度文章。無論你是AI…

Business Magic

題目描述There are n stores located along a street, numbered from 1 to n from nearest to farthest. Last month, the storek had a net profit of rk . If rk is positive, it represents a profit of rk dollars; if rk is negative, it represents a loss of ?rk dolla…

在ubuntu系統上離線安裝jenkins的做法

作者:朱金燦 來源:clever101的專欄 1.安裝java環境和下載war包: Jenkins 依賴于 Java 環境(OpenJDK 11 或更高版本): # 安裝OpenJDK 11和字體依賴 sudo dpkg -i openjdk-11-jre-headless_*.deb fontconfi…

圖像相似度算法匯總及Python實現

下面整理了一些圖像相似度算法,可根據不同的需求選擇不同的算法,對每種算法進行了簡單描述并給出Python實現: 1. 基于像素的算法: (1).MSE(Mean Squared Error):均方誤差,通過計算兩幅圖像對應像素值差的平…

IO流與單例模式

單例模式 單例模式是指一個類只能有一個對象。 餓漢模式 在單例模式下,在程序開始(main函數運行前)的時候創建一個對象,這之后就不能再創建這個對象。 class HungryMan { public:static HungryMan* getinstance(){return &ins…

Java設計模式之依賴倒置原則使用舉例說明

示例1:司機駕駛汽車 問題場景:司機類直接依賴奔馳車類,新增寶馬車需修改司機類代碼。 // 未遵循DIP class Benz { public void run() { /*...*/ } } class Driver { public void drive(Benz benz) { benz.run(); } } // 遵循DIP:…

【Docker】openEuler 使用docker-compose部署gitlab-ce

docker-compose配置 services:gitlab:image: gitlab/gitlab-ce:latestcontainer_name: gitlabrestart: alwayshostname: gitlab.example.comenvironment:GITLAB_OMNIBUS_CONFIG: |# Add any other gitlab.rb configuration here, each on its own lineexternal_url https://gi…

ElasticSearch 父子文檔使用簡記

一. ES parent-child 文檔簡介 ES 提供了類似數據庫中 Join 聯結的實現,可以通過 Join 類型的字段維護父子關系的數據,其父文檔和子文檔可以單獨維護。 二. 父子文檔的索引創建與數據插入 ES 父子文檔的創建可以分為下面三步: 創建索引 M…

【Linux】編輯器vim的使用

目錄 1. vim的基本概念 2. vim的基本使用 3. vim命令模式操作 3.1 移動光標 3.2 刪除 3.3 復制 3.4 替換 3.5 撤銷 3.6 更改 3.7 跳轉 4. vim底行模式操作 4.1 列出行號 4.2 跳到文件中的某行 4.3 查找字符 4.4 保存文件 4.5 離開vim 1. vim的基本概念 Vim&…

《零基礎掌握飛算Java AI:核心概念與案例解析》

前引:飛算科技是一家專注于企業級智能化技術服務的公司,核心領域包括AI、大數據、云計算等。其Java AI解決方案主要面向企業級應用開發,提供從數據處理到模型部署的全流程支持!飛算Java AI是一款基于人工智能技術的Java開發輔助工…

Chrome騰訊翻譯插件transmart的安裝

文章目錄一、官網地址二、安裝過程1. 下載插件2. 解壓crx3, chrome安裝三、如何使用一、官網地址 騰訊翻譯插件官網 二、安裝過程 1. 下載插件 點擊上面的官網地址,下拉到如圖所示chrome插件位置,點擊立即下載 2. 解壓crx 從壓縮文件中解壓出crx文…

IOMMU的2級地址翻譯機制及多級(2~5)頁表查找

IOMMU的2級地址翻譯機制及多級(2~5)頁表查找 摘要:IOMMU是現代計算機系統中用于I/O設備(如GPU、NIC、網絡接口卡)的地址翻譯和保護機制,類似于CPU的MMU(Memory Management Unit),但專為設備DMA(Direct Memory Access,直接內存訪問)設計。它支持虛擬化環境(…

C++STL標準模板庫詳解

一、引言STL(Standard Template Library)是 C 標準庫的核心組成部分,其中容器(Containers) 作為數據存儲的基礎組件,為開發者提供了豐富的數據結構選擇。本文將聚焦 STL 容器的核心類型,結合具體…

神經網絡 常見分類

📚 神經網絡的常見分類方式可以從不同角度來劃分,以下是幾種主流思路,幫你快速梳理清晰:1?? 按網絡結構分類前饋神經網絡(Feedforward Neural Network, FNN) 數據從輸入層→隱藏層→輸出層單向傳遞&#…

生產環境Redis緩存穿透與雪崩防護性能優化實戰指南

生產環境Redis緩存穿透與雪崩防護性能優化實戰指南 在當下高并發場景下,Redis 作為主流緩存組件,能夠極大地提升讀寫性能,但同時也容易引發緩存穿透、緩存擊穿及緩存雪崩等問題,導致后端依賴數據庫的請求激增,系統穩定…

【洛谷刷題】用C語言和C++做一些入門題,練習洛谷IDE模式:分支機構(一)

🔥個人主頁:艾莉絲努力練劍 ?專欄傳送門:《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題、洛谷刷題、C/C基礎知識知識強化補充、C/C干貨分享&學習過程記錄 🍉學習方向:C/C方向 ??人…

嵌入式硬件篇---常見的單片機型號

以下是目前常用的單片機型號及其應用場景、優劣勢的詳細解析,結合最新行業動態和技術特性,幫助你精準匹配需求:一、經典 8 位單片機:低成本入門首選1. 51 系列(代表型號:AT89C51、STC89C52)應用…