JAVA JVM垃圾收集

JVM 垃圾收集是 Java 自動內存管理的核心,本文通過圍繞 “哪些是垃圾、何時回收、怎么回收、用啥回收器、內存咋分配” 等展開

一、判斷哪些是垃圾

  • 引用計數法:給對象分配引用計數器,有引用時計數加 1,引用失效減 1 ,計數為 0 則可回收。但無法解決循環引用問題(如兩個對象相互引用,實際無外部引用,計數器卻不為 0 )。
Java虛擬機并不是通過引用計數算法來判斷對象是否存活的
  • 可達性分析:以 GC Roots(如虛擬機棧中引用的對象、方法區靜態變量引用的對象等 )為起點,遍歷對象引用鏈,沒被鏈連接的對象視為可回收垃圾,主流 JVM 常用此方式。
    • 枚舉根節點:需暫停應用線程(“stop the world” ),因遍歷期間對象引用變化會影響結果,所以要讓線程停頓,后續有優化手段(如并發標記 )緩解停頓影響。
  • 引用分類:
    • 強引用 程序正常引用,如?Object obj = new Object()?,只要強引用在,對象不回收
    • 軟引用(內存不足時回收,可配合緩存場景 )用來描述一些還有用,但非必須的對象。只被軟引用關聯著的對象,在系統將要發生內存溢出異常前,會把這些對象列進回收范圍之中進行第二次回收,如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。
    • 弱引用(垃圾收集時就回收 )也是用來描述那些非必須對象,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生為止。當垃圾收集器開始工作,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。
    • 虛引用(主要用于跟蹤對象回收,回收前收到系統通知 )為一個對象設置虛引用關聯的唯一目的只是為了能在這個對象被收集器回收時收到一個系統通知。
即使在可達性分析算法中判定為不可達的對象,也不是“非死不可”的,要真正宣告一個對象死亡,至少要經歷兩次標記過程:如果對象在進行可達性分析后發現沒有與GC Roots相連接的引用鏈,那它將會被第一次標記,隨后進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法。假如對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過,那么虛擬機將這兩種情況都視為“沒有必要執行”。

二、?垃圾回收時機與分代收集理論

回收時機:新生代(Minor GC ):Eden 區快滿時觸發,回收新生代垃圾,借助復制算法,把存活對象移到 Survivor 或老年代;老年代(Major GC/Full GC ):老年代空間不足、永久代(元空間 )滿等情況觸發,回收老年代及可能涉及新生代,用標記 - 整理或標記 - 清除(結合壓縮 ),Full GC 耗時久,盡量避免。
分代收集建立在兩個分代假說之上:
1)弱分代假說(Weak Generational Hypothesis):絕大多數對象都是朝生夕滅的。
2)強分代假說(Strong Generational Hypothesis):熬過越多次垃圾收集過程的對象就越難以消亡
這兩個分代假說共同奠定了多款常用的垃圾收集器的一致的設計原則:收集器應該將Java堆劃分出不同的區域,然后將回收對象依據其年齡(年齡即對象熬過垃圾收集過程的次數)分配到不同的區域之中存儲。
設計者一般至少會把Java堆劃分為新生代(Young Generation)和老年代(Old Generation)兩個區域,在新生代中,每次垃圾收集時都發現有大批對象死去,而每次回收后存活的少量對象,將會逐步晉升到老年代中存放。
  • 大部分對象朝生夕死:新生代特點,對應 “標記復制” 算法思路,如 Eden 區 + Survivor 區,新對象先放 Eden ,回收時把存活對象復制到 Survivor ,減少內存碎片。
  • 少數對象存活較久:老年代特征,常用 “標記清除”“標記整理” 算法。“標記清除” 先標記可回收對象,再清除,會產生內存碎片;“標記 - 整理” 標記后讓存活對象移動、緊湊排列,解決碎片問題,但需額外移動成本。
  • 跨代引用假說:老年代對象引用新生代對象,比新生代內部引用少。垃圾收集時,若掃描老年代找跨代引用,效率低。可通過在新生代設記憶集(記錄老年代到新生代的引用 ),當發生Minor GC時,只有包含了跨代引用的小塊內存里的對象才會被加入到GC Roots進行掃描,避免全掃老年代,優化回收效率。

三、垃圾回收算法(收集算法 )

  • 標記清除:分標記、清除階段,標記出可回收對象,再清理內存。缺點是1.產生碎片,可能導致大對象無法分配連續內存2.執行效率不穩定。
  • 標記復制:將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。實現簡單,運行高效,無空間碎片,不過這種復制回收算法的代價是將可用內存縮小為了原來的一半
現在的商用Java虛擬機大多都優先采用了這種收集算法去回收新生代,IBM公司曾有一項專門研究對新生代“朝生夕滅”的特點做了更量化的詮釋——新生代中的對象有98%熬不過第一輪收集。因此并不需要按照1∶1的比例來劃分新生代的內存空間。
在1989年,Andrew Appel針對具備“朝生夕滅”特點的對象,提出了一種更優化的半區復制分代策略,現在稱為“Appel式回收”。
Appel式回收的具體做法是把新生代分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次分配內存只使用Eden和其中一塊Survivor。發生垃圾搜集時,將Eden和Survivor中仍然存活的對象一次性復制到另外一塊Survivor空間上,然后直接清理掉Eden和已用過的那塊Survivor空間。HotSpot虛擬機默認Eden和Survivor的大小比例是8∶1,也即每次新生代中可用內存空間為整個新生代容量的90%(Eden的80%加上一個Survivor的10%),只有一個Survivor空間,即10%的新生代是會被“浪費”的。
  • 標記整理:標記后,讓存活對象向一端移動,整理內存,解決碎片問題,適用于老年代等對象存活率高區域,不過移動對象有性能開銷,還需處理對象引用更新。
  • 分代收集:結合不同代特點選算法,新生代用標記 - 復制,老年代用標記 - 清除 / 標記 - 整理,是 JVM 常用策略,如 HotSpot 虛擬機的分代垃圾收集器(Serial、ParNew、Parallel Scavenge 對應新生代,Serial Old、Parallel Old、CMS、G1 等涉及老年代 )。

四、垃圾收集器(不同實現 )

了解
  • Serial(串行 ):新生代、老年代都可用,單線程工作,“Stop - The - World” 明顯,簡單高效,適合客戶端模式、內存小場景。
  • ParNew:Serial 多線程版,用于新生代,配合 CMS 老年代收集器(CMS 新生代需用 ParNew 或 Serial ),多線程加速新生代回收,在服務端應用常見。
  • Parallel Scavenge:新生代收集器,關注吞吐量(運行用戶代碼時間 /(用戶代碼時間 + 垃圾收集時間 )),適合后臺計算等對吞吐量敏感場景,可自動調節參數(自適應調節策略 )。
  • Serial Old:Serial 老年代版本,單線程,標記 - 整理算法,可與 Parallel Scavenge 配合,或作為 CMS 后備預案(CMS 并發失敗時啟用 )。
  • Parallel Old:Parallel Scavenge 老年代版,多線程、標記 - 整理算法,讓吞吐量優先的收集器組合(Parallel Scavenge + Parallel Old )更完善,適合追求高吞吐量場景。
主流
  • CMS(Concurrent Mark Sweep):

特點:以 “并發” 為核心,標記和清除階段可與應用線程并行(減少 stop the world 時間 );基于 “標記 - 清除” 算法。

問題:會產生內存碎片;并發階段占用 CPU 資源,可能影響應用;無法處理 “浮動垃圾”(并發階段新產生的垃圾,需等下次回收 )。

適用:追求低延遲、對吞吐量要求不極致的場景(如 Web 應用 )。

  • G1(Garbage - First):

特點:面向服務端應用,把堆劃分為多個 Region;基于 “標記 - 整理”,按 Region 回收,優先回收垃圾多的 Region(“Garbage - First” );可預測停頓時間(通過設置停頓目標,規劃回收 Region )。

優勢:兼顧吞吐量和延遲,適合大內存場景;減少碎片。

適用:對停頓敏感、堆內存較大的應用(如大型后臺服務 )。

五、內存分配與回收策略

  • 對象優先在 Eden 分配:新生代 Eden 區是對象誕生地,新對象先放這,Eden 滿觸發 Minor GC。
  • 大對象直接進老年代:大對象(如超長數組 )不適合在新生代折騰,直接分配到老年代,避免多次 GC 拷貝。
  • 長期存活的進入老年代:對象在新生代 Survivor 區經歷多次 Minor GC 仍存活(通過 “年齡計數器” 判斷 ),會晉升到老年代。
  • 動態年齡判定:并非等 “年齡” 到閾值才晉升,若 Survivor 中同年齡對象總和超過該區一半,年齡≥此值的對象直接進老年代,靈活調整。
  • 空間分配擔保:Minor GC 前,JVM 判斷老年代剩余空間是否夠放新生代存活對象。若預估夠,執行 Minor GC;否則看 “擔保” 機制,允許則嘗試,不允許則轉為 Full GC ,保障內存分配安全。

六、垃圾回收相關問題

  • 空間碎片問題:標記 清除易產生,影響大對象分配,標記整理、標記復制可緩解,不同收集器處理方式不同(如 G1 靠 Region 劃分和整理,減少碎片影響 )。
  • “Stop the world”:垃圾收集時暫停用戶線程,避免對象引用變化干擾回收,不同收集器盡力縮短停頓(如 G1 并發標記、CMS 并發階段 ),但關鍵步驟(如根節點枚舉 )仍需停頓,是垃圾收集需優化的點。
  • 什么時候回收:沒有固定嚴格時間,JVM 依堆內存使用、分代特點等判斷。新生代對象滿了觸發 Minor GC ,老年代空間不足、永久代(元空間 )不足等觸發 Full GC ,不同收集器觸發機制有差異,且要平衡回收頻率和性能,避免頻繁回收影響應用。

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

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

相關文章

UniHttp生命周期鉤子與公共參數實戰:打造智能天氣接口客戶端

> 通過靈活的生命周期鉤子,我們讓HTTP請求從機械操作進化為智能對話 在現代應用開發中,高效處理HTTP請求是核心能力。本文將深入探索UniHttp框架中強大的**HttpApiProcessor生命周期鉤子**,并演示如何利用其**公共參數填充機制**優雅地處理第三方接口。我們將以百度天…

C++高級編程,類模版成員函數類外實現

#include <iostream> #include <string>//類模版成員函數類外實現 template<class T1,class T2> class Person {//Person構造函數 public:Person(T1 name,T2 age);// {// this->m_Namename;// this->m_Ageage;// }//Person的成員函數void show…

[Linux入門 ] RAID存儲技術概述

一.數據存儲架構 1??存儲系統 2??主機系統 3??互連部件 4??存儲設備與磁盤陣列 二.數據存儲技術 1??數據冗余技術 2??RAID 0 3??RAID 1 4??RAID 2 5??RAID 3 6??RAID 4 三.基于硬件的RAID磁盤陣列 1??陣列卡(RAID控制器) 2??陣列卡種類 …

AI繪畫生成章邯全身像提示詞

融合了歷史元素和視覺表現力&#xff0c;力求生成符合秦末名將章邯身份的全身像。 核心提示詞結構&#xff1a; [主體描述]&#xff0c;[服裝/盔甲細節]&#xff0c;[姿態/神情]&#xff0c;[武器]&#xff0c;[背景/氛圍]&#xff0c;[風格/質量]&#xff0c;[參數] 選項一&…

iOS高級開發工程師面試——關于優化

iOS高級開發工程師面試——關于優化 一、TableView 有什么好的性能優化方案?二、界面卡頓和檢測你都是怎么處理?三、談談你對離屏渲染的理解?四、如何降低APP包的大小?五、日常如何檢查內存泄露?六、APP啟動時間應從哪些方面優化?一、TableView 有什么好的性能優化方案?…

線性基學習筆記

我們稱一個線性空間 V V V 的一個極大線性無關集為這個線性空間的線性基,簡稱基。 異或線性基 在異或空間下,我們定義如下內容。 異或和 設 S S

ESP-Timer入門(基于ESP-IDF-5.4)

主要參考資料&#xff1a; ESP 定時器&#xff08;高分辨率定時器&#xff09;: https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32s3/api-reference/system/esp_timer.html 目錄ESP-Timer與FreeRTOS TimerAPI 使用1.創建定時器2.啟動定時器3.管理定時器4.時間管…

014_批處理與大規模任務

批處理與大規模任務 目錄 批處理概述核心優勢技術規格API使用管理和監控應用場景最佳實踐 批處理概述 什么是批處理 批處理&#xff08;Batch Processing&#xff09;是一種異步處理大量Claude API請求的方法&#xff0c;允許您一次性提交多個消息請求&#xff0c;系統將在…

Python淘寶拍立淘按圖搜索API接口,json數據示例參考

淘寶拍立淘按圖搜索API接口示例淘寶的拍立淘(圖片搜索)功能通常是通過淘寶開放平臺提供的API實現的。以下是一個模擬的JSON數據示例和接口調用參考&#xff1a;模擬API請求示例import requestsimport base64# 示例圖片路徑image_path "example.jpg"# 讀取圖片并編碼…

靜默的田野革命—人工智能重構農業生態的技術風暴與文明悖論

一、饑餓困局的數字突圍當全球糧食損失率高達30%&#xff08;約13億噸&#xff09;與8億人營養不良并存&#xff0c;當農藥濫用導致傳粉昆蟲種群崩潰與地下水資源枯竭&#xff0c;傳統農業的生態死結日益收緊。這場危機的核心是生物復雜性對工業化農業的報復&#xff1a;小麥基…

【大模型推理論文閱讀】 Thinking Tokens are Information Peaks in LLM Reasoning

Demystifying Reasoning Dynamics with Mutual Information&#xff1a;Thinking Tokens are Information Peaks in LLM Reasoning 摘要 大語言推理模型&#xff08;LRM&#xff09;在復雜問題解決方面展現出了令人矚目的能力&#xff0c;但其內部推理機制仍未得到充分理解。…

【TCP/IP】14. 遠程登錄協議

14. 遠程登錄協議14. 遠程登錄協議14.1 基本概念14.2 Telnet 命令14.3 Telnet 選項及協商14.4 Telnet 子選項協商14.5 Telnet 操作模式本章要點14. 遠程登錄協議 14.1 基本概念 Telnet 協議是 TCP/IP 協議族的重要成員&#xff0c;核心功能是實現本地計算機對遠程主機的終端仿…

Flink1.20.1集成Paimon遇到的問題

flinkcdc mysql 到paimon 1&#xff1a;Caused by: java.lang.ClassNotFoundException: org.apache.kafka.connect.data.Schema 可以參考這個文章 明確指出了flink-connector-mysql-cdc-3.4.0.jar存在這個包&#xff0c;但是flink-sql-connector-mysql-cdc-3.4.0.jar中沒有這個…

C++高頻知識點(十)

文章目錄46. 智能指針是什么&#xff1f;怎么使用?1. std::unique_ptr2. std::shared_ptr3. std::weak_ptr47. 什么是野指針&#xff1f;1. 使用已釋放的指針2. 未初始化的指針3. 指針超出作用域如何避免野指針1. 立即將指針置空2. 初始化指針3. 使用智能指針4. 避免返回局部變…

c#中Random類、DateTime類、String類

C# 中 Random 類分析Random 類用于生成偽隨機數&#xff0c;位于 System 命名空間。它的核心機制是基于一個種子值 (seed)&#xff0c;通過算法生成看似隨機的數列。相同種子會生成相同的隨機數序列&#xff0c;這在需要可重現的隨機場景中很有用。核心特點種子與隨機性默認構造…

Vscode 下載遠程服務器失敗解決方法

今天在使用 vscode 連接遠程主機時&#xff0c;突然再次遇到這個問題&#xff0c;按照以往的經驗&#xff0c;直接按照這個博主的文章其實就能解決&#xff0c;但是不知道為什么&#xff0c;今天這個方案失效了&#xff0c;然后卸載安裝服務器和本機的vscode什么的也都試過了&a…

【算法】貪心算法:檸檬水找零C++

文章目錄前言題目解析算法原理代碼示例策略證明前言 題目的鏈接&#xff0c;大家可以先試著去做一下再來看一下思路。 860. 檸檬水找零 - 力扣&#xff08;LeetCode&#xff09; 題目解析 首先我們要認真去拿到題目中的關鍵有用信息。 認真的去閱讀題目給的示例&#xff0c;然…

27.【.NET8 實戰--孢子記賬--從單體到微服務--轉向微服務】--單體轉微服務--幣種服務(一)

從本篇文章開始&#xff0c;我們將用兩篇內容詳細介紹幣種服務的實現。幣種服務本身結構較為簡單&#xff0c;核心功能包括內置幣種的初始化、幣種匯率的同步以及匯率的查詢。在本篇中&#xff0c;我們將重點講解如何實現內置幣種的初始化功能&#xff0c;為后續的服務打下基礎…

(2)從零開發 Chrome 插件:實現 API 登錄與本地存儲功能

從零開發 Chrome 插件&#xff1a;實現 API 登錄與本地存儲功能 Chrome 插件作為瀏覽器功能的重要擴展&#xff0c;能極大提升用戶的工作效率。本文將以一個「登錄功能插件」為例&#xff0c;帶你從零構建一個可調用 API 驗證身份、并將用戶信息存儲在本地的 Chrome 插件。 基…

Flink時間窗口詳解

一、引言在大數據流處理的領域中&#xff0c;Flink 的時間窗口是一項極為關鍵的技術&#xff0c;想象一下&#xff0c;你要統計一個電商網站每小時的訂單數量。由于訂單數據是持續不斷產生的&#xff0c;這就形成了一個無界數據流。如果沒有時間窗口的概念&#xff0c;你就需要…