3 垃圾收集演算法

来源:https://www.cnblogs.com/knowledgeispower/archive/2022/09/20/16712317.html
-Advertisement-
Play Games

1 垃圾收集三件事 哪些記憶體需要回收:死去的對象需要回收 什麼時候回收 如何回收 按照jvm記憶體區域劃分原則:程式計數器、虛擬機棧、本地方法棧3個區域的記憶體隨線程創建而劃分,因此線程結束時,記憶體也自動釋放。 本章節分析的是Java堆和方法區的記憶體管理策略 1、虛擬機棧、本地方法棧,棧中的棧幀隨著方法 ...


目錄

1 垃圾收集三件事

  1. 哪些記憶體需要回收:死去的對象需要回收
  2. 什麼時候回收
  3. 如何回收

按照jvm記憶體區域劃分原則:程式計數器、虛擬機棧、本地方法棧3個區域的記憶體隨線程創建而劃分,因此線程結束時,記憶體也自動釋放。
本章節分析的是Java堆和方法區的記憶體管理策略

1、虛擬機棧、本地方法棧,棧中的棧幀隨著方法的進入和退出而有條不紊地執行著出棧和入棧操作。
  每一個棧幀中分配多少記憶體基 本上是在類結構確定下來時就已知的(儘管在運行期會由即時編譯器進行一些優化,但在基於概念模 型的討論里,大體上可以認為是編譯期可知的),因此這幾個區域的記憶體分配和回收都具備確定性。
2、堆和方法區這兩個區域則有著很顯著的不確定性:一個介面的多個實現類需要的記憶體可能會不一樣,一個方法所執行的不同條件分支所需要的記憶體也可能不一樣,
只有處於運行期間,我們才能知道程式究竟會創建哪些對象,創建多少個對象,這部分記憶體的分配和回收是動態的。

2 對象存活判定演算法

回收堆,也就是回收對象,判斷對象是否需要回收,也就是判斷對象是否死亡,有兩種策略:引用計數演算法和可達性分析演算法

2.1 引用計數演算法

在對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加一;當引用失效時,計數器值就減一;任何時刻計數器為零的對象就是不可能再被使用的

缺點

  1. 當對象存在相互引用時,該判斷方法失效
  2. 當前較少java虛擬機應用該演算法

關於引用的說明

分類 定義 垃圾回收
強引用 程式代碼之中普遍存在的引用賦值 不回收
軟引用 一些還有用,但非必須的對象。SoftReference修飾 將要發生溢出時,才會被回收
弱引用 強度比軟引用 弱一點。 WeakReference 修飾 垃圾收集器啟動,就會被回收,而不管是否發生溢出
虛引用 目的只是為了能在這個對象被收集器回收時收到一個系統通知 垃圾收集器啟動,就會被回收 【設置虛引用的目的僅是為了在對象被回收時收到一個通知】

2.2 可達性分析演算法

通過GC Root節點,根據引用關係向下遍歷,當存在對象不在引用連上,則該對象可能不在被引用。
註意:當前下,根節點選舉,還是需要暫停所有用戶線程,以便保證快照一致性

在Java技術體系裡面,固定可作為GC Roots的對象包括以下幾種:

  • 虛擬機棧中引用的對象,譬如各個線程被調用的方法堆棧中使用到的參數、局部變數、臨時變數等
  • 方法區中類靜態屬性引用的對象,譬如Java類的引用類型靜態變數
  • 方法區中常量引用的對象,譬如字元串常量池裡的引用
  • 本地方法棧中Native方法引用的對象
  • 所有被同步鎖(synchronized)持有的對象

2.2.1 不可達對象的後置處理

當對象被判斷為不可達對象後,它仍有可能不被回收:調用了finalize()方法並且在方法里調用其它存活對象
因此,不可達對象在第一次標誌後,還會有一個執行判斷過程:

  1. 當對象被判定為不可達對象後,進行第一次標記。
  2. 對已經被標記的對象篩選出來,判斷是否需要執行finalize()方法,需要就放到執行隊列裡面。
  3. 在finalize(),如果產生對存活對象的引用,jvm會將該不可達對象移除待回收的集合。

過程如下所示:

關於finalize()方法

  1. 它的運行代價高昂,不確定性大,無法保證各個對象的調用順序,因此不推薦使用
  2. finalize()能做的所有工作,使用try-finally或者其他方式都可以做得更好

2.3 方法區回收判定

  1. 方法區的回收條件比較苛刻,成本高,《Java虛擬機規範》不要求實現方法區域的垃圾回收
  2. HotSpot虛擬機中的元空間或者永久代是沒有垃圾收集行為
  3. 方法區的垃圾收集主要回收兩部分內容:廢棄的常量不再使用的類型

一、判斷常量是否被廢棄

  1. 沒有任何對象引用常量池中的這個常量
  2. 虛擬機中也沒有其他地方引用這個常量

二、判斷類型是否不再使用

  1. 類所有的實例都已經被回收,也就是Java堆中不存在該類及其任何派生子類的實例
  2. 載入該類的類載入器已經被回收
  3. 類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法
Java虛擬機被允許對滿足上述三個條件的無用類進行回收,這裡說的僅僅是“被允許”,而並不是 和對象一樣,沒有引用了就必然會回收。
關於是否要對類型進行回收,HotSpot虛擬機提供了- Xnoclassgc參數進行控制,還可以使用-verbose:class以及-XX:+TraceClass-Loading、-XX: +TraceClassUnLoading查看類載入和卸載信息

5 垃圾收集演算法介紹

5.1 分代收集理論

!!!重要重要重要

  1. 將堆記憶體按照區域劃分,存儲不同"年齡"對象。(即分為新生代老年代);
  2. 新生代對象可以轉到老年代去;
  3. 對於新生代,回收時只關註少量需要保留的對象;
  4. 對於老年代,使用低頻率來進行回收;
  5. 由於分區的出現,促使回收可以針對特定區域進行,或者不同的區域使用不同的回收演算法。
  6. 對於跨代對象,在新生代建立記憶表,存儲老年代哪些區域存在跨帶引用,在回收處理時,僅處理該區域的對象;

關於收集的補充說明
部分收集(Partial GC):指目標不是完整收集整個Java堆的垃圾收集,分為:

  • 老年代收集(Major GC/Old GC):指目標只是老年代的垃圾收集。
  • 混合收集(Mixed GC):指目標是收集整個新生代以及部分老年代的垃圾收集
  • 整堆收集(Full GC):收集整個Java堆和方法區的垃圾收集。

5.1.1 記憶集與卡表

  1. 記憶集的目的:解決跨帶引用帶來的可能要掃描整個老年代的問題
  2. 它是一個存儲在非收集區的指針集合的數據結構,元素指向收集區
  3. 卡表:記憶集精度到記憶體區域,稱為卡表;抽象為一個位元組數組
  4. 卡頁:卡表的一個元素:記憶體塊:存儲一個記憶體地址,根據指定頁碼的大小(2的N次冪位元組數),構成地址範圍;
  5. 一個卡頁的記憶體中通常包含不止一個對象,只要卡頁內有一個(或更多)對象的欄位存在著跨代 指針,那就將對應卡表的數組元素的值標識為1,稱為這個元素變臟(Dirty),沒有則標識為0。在垃 圾收集發生時,只要篩選出卡表中變髒的元素,就能輕易得出哪些卡頁記憶體塊中包含跨代指針,把它 們加入GC Roots中一併掃描。

5.2 標記-清除演算法

  1. 標記出所有待回收對象
  2. 對被標記對象進行清除
  3. 缺點:1、如果有大量待清除對象,則出現多次的標記-清除操作(我理解是既然被清除,則無需進行標記);2、產生記憶體碎片

5.3 標誌-複製演算法

  1. 將記憶體同等劃分2個區域,新對象都被放在其中一個區域A;
  2. 當該區域記憶體用完後,將活著的對象複製到另外一塊區域中B;
  3. 將已使用過半區進行回收清除;同時新對象只會出現在區域B中;
  4. 優點:不會產生記憶體碎片(存活對象被放在一起,待回收對象被整塊清除)
  5. 缺點:1、複製會產生開銷;2、記憶體浪費:可用記憶體只剩一半

5.4 優化後的標誌-複製演算法

  1. 將新生代跨分成三個區域:一大:Eden,兩小Survivor。分別占位10:8 10:1 10:1,新產生的對象隨機進入使用Eden和其中一塊正在使用的Survivor
  2. 發生垃圾搜集時,將Eden和Survivor中仍然存活的對象一次性複製到另外一塊Survivor空間上,然後直接清理掉Eden和已用過的那塊Survivor
  3. 當Survivor不足以存放存活對象時,轉入到入老年代。如果對象經過經過18GC後,還存活,那麼也會轉入到老年代

過程如圖所示:

5.5 標記-整理演算法

  1. 其中的標記過程仍然與“標記-清除”演算法一樣;
  2. 不對可回收對象直接回收,而是讓所有存活的對象都向記憶體空間一端移動,然後直接清理掉邊界以外的記憶體
  3. 缺點:對象被移動,需要更新其引用,需要停止所有運行線程

過程如圖所示:

6 根節點枚舉

6.1 關於根節點及其枚舉

  1. 可以作為根節點的數據集中在:方法區【量池、靜態變數】、虛擬機棧:【本地變數表】
  2. 根節點枚舉,需要暫停用戶線程
  3. 另外,查找引用鏈過程已經實現了跟用戶線程併發

6.2 oopMap數據結構

oopMap是什麼?

  1. oopMap是一個數據結構,它存儲的內容可以作為根節點。
  2. jvm執行某些位元組碼指令時,會創建該數據結構

為什麼需要oopMap?

  1. 優點:通過oopMap,jvm可以直接找到對象的引用, jvm不需要一個不漏地檢查完所有 執行上下文和全局的引用位置,從而快速地完成根節點枚舉
  2. 缺點:引起OopMap內容變化的指令非常多,如果為每一條指令都生成對應的oopMap,那將會需要大量的額外存儲空間

oopMap如何實現

  1. 類載入完成後,保存對象的屬性的偏移地址。【備註一】
  2. 即時編譯時,保存棧里對象的引用地址【備註二】

6.3 安全點

安全點是什麼

  1. 編譯生成位元組碼指令時,只針對特定的指令,才創建oopMap,我們把這些特定指令的地址稱為安全點。

為什麼需要安全點?

  1. 如果對所有的指令都生成oopMap,會耗費大量的空間;選擇在安全點位置創建oopMap,節省記憶體空間
  2. 有了安全點的設定,也就決定了用戶程式執行時並非在代碼指令流的任意位置都能夠停頓下來開始垃圾收集,而是強制要求必須執行到達安全點後才能夠暫停。

安全點如何實現?

  1. 安全點位置的選取在具備讓程式長時間執行的復用型指令,例如方法調用、迴圈跳轉、異常跳轉 ,只有具有這些功能的指令才會產生安全點;
  2. JVM使用主動式中斷方案,讓線程暫停執行,來響應GC事件。

關於主動式中斷

  1. 當垃圾收集需要中斷線程的時候,不直接對線程暫停,僅設置一個標誌位,各個線程執行時輪詢這個標誌,一旦發現中斷標誌為真時就自己在最近的安全點上主動中斷掛起
另一種方案是搶先式中斷:【主流虛擬機不使用該方案】
搶先式中斷不需要線程的執行代碼 主動去配合,在垃圾收集發生時,系統首先把所有用戶線程全部中斷,如果發現有用戶線程中斷的地 方不在安全點上,就恢復這條線程執行,讓它一會再重新中斷,直到跑到安全點上

6.4 安全區域

安全區域是什麼?

  1. 在該代碼片中,對象引用關係不會發生變化,那麼這塊代碼(或者位元組碼指令片段)就是安全區域

為什麼需要安全區域?

  1. 線程沒有分配cpu時間時(處於Sleep狀或Blocked狀態)無法響應虛擬機的中斷請求,不能再走到安全的地方去中斷掛起自己,安全點的設置就不起效,因此需要安全區域。
  2. 在安全區域進行垃圾收集是安全的。

安全區域對線程和回收器的影響

  1. 當用戶線程執行到安全區域裡面的代碼時,首先會標識自己已經進入了安全區域。
  2. 虛擬機要發起垃圾收集時,將不會給處於安全區域的線程打上暫停標誌【對應主動式中斷的打標識】
  3. 當線程要離開安全區域時,它要檢查虛擬機是否已經完成了根節點枚舉:
    1. 完成:線程就當作沒事發生過,繼續執行
    2. 未完成:線程一直等待,直到收到可以離開安全區域的信號為止。

用一張圖來描述:

6.5 oopMap、安全點、安全區域對比總結

用一張圖來總結:

7 可達性遍歷的併發分析

未完成待續


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

-Advertisement-
Play Games
更多相關文章
  • Java面向對象 1.類和對象 1.1 類和對象的概念: 類是抽象的集合,對象是具體的實例。 類可以想象為製作蛋糕的模具,對象就是做出來的蛋糕。 類中包含屬性(欄位)和方法(操作) 1.2 類的定義: Class ClassName { 屬性1 屬性2 ··· 構造器1 構造器2(如果不寫,系統會默 ...
  • 寫程式之前要瞭解兩個概念 1.什麼是進程 2.什麼是線程 搞清楚這兩個概念之後 才能寫好一個合適而不會太抽象的程式 對進程和線程的理解見鏈接: https://blog.csdn.net/new_teacher/article/details/51469241 https://www.cnblogs ...
  • form表單內容序列化 form表單自帶兩種方法serialize()方法和serialize()方法 1.serialize()方法 描述:序列化表單內容為字元串(不包括文件),用於Ajax請求。 格式:var data = $('#form').serialize(); 2.serializeA ...
  • 我的gRPC之旅。使用gRPC一元通信模式和雙向流通信模式寫一個簡單的控制台聊天室。實現創建用戶和實時聊天兩個功能,不考慮高性能。複習了記憶體同步訪問Sync包的使用。用切片緩存聊天記錄,新用戶可以同步聊天記錄。 ...
  • Java基礎知識 Java的三種版本 JavaSE :標準版,主要用於開發桌面程式,控制台開發等等 JavaME:嵌入式開發,主要用於開發手機,小家電等等,目前使用的比較少 JavaEE:企業級開發,主要用於web端開發,伺服器開發等等,是使用十分廣泛的,學好這部分就要學好JavaSE JDK、JR ...
  • 來源:liuchenyang0515.blog.csdn.net/article/details/109263510 對稱加密 兩邊用同一個密鑰來加解密。 A把明文通過某一演算法加密之後得到密文,然後把密文發送給B,B接收到密文之後用相同的密鑰執行相同的演算法去解密。X沒有密鑰,即使竊取到密文也無法竊聽 ...
  • 多用戶即時通訊系統01 1.項目開發流程 2.需求分析 用戶登錄 拉取線上用戶列表 無異常退出(包括客戶端和服務端) 私聊 群聊 發文件 伺服器推送新聞/廣播 3.設計階段 3.1界面設計 用戶登錄: 拉取線上用戶列表: 私聊: 群聊: 發文件: 文件伺服器推送新聞: 3.2通訊系統整體設計 總結: ...
  • 實時展示用戶上傳的頭像 總體思路 """ 1.首先需要給對應的上傳頭像input框綁定一個文本域變化事件 (當檢測到用戶對該文件框上傳了頭像就會觸發一系列操作) 2.再生成一個文件閱讀器對象 3.再獲取用戶上傳的文件頭像 4.把用戶上傳的文件頭像交給文件閱讀器對象FileReader讀取 5.利用文 ...
一周排行
    -Advertisement-
    Play Games
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 目錄簡介快速入門安裝 NuGet 包實體類User資料庫類DbFactory增刪改查InsertSelectUpdateDelete總結 簡介 NPoco 是 PetaPoco 的一個分支,具有一些額外的功能,截至現在 github 星數 839。NPoco 中文資料沒多少,我是被博客園群友推薦的, ...
  • 前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...