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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...