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
  • 一:背景 1.講故事 在分析的眾多dump中,經常會遇到各種奇葩的問題,僅通過dump這種快照形式還是有很多問題搞不定,而通過 perfview 這種粒度又太粗,很難找到問題之所在,真的很頭疼,比如本篇的 短命線程 問題,參考圖如下: 我們在 t2 時刻抓取的dump對查看 短命線程 毫無幫助,我根 ...
  • 在日常後端Api開發中,我們跟前端的溝通中,通常需要協商好入參的數據類型,和參數是通過什麼方式存在於請求中的,是表單(form)、請求體(body)、地址欄參數(query)、還是說通過請求頭(header)。 當協商好後,我們的介面又需要怎麼去接收這些數據呢?很多小伙伴可能上手就是直接寫一個實體, ...
  • 許多情況下我們需要用到攝像頭獲取圖像,進而處理圖像,這篇博文介紹利用pyqt5、OpenCV實現用電腦上連接的攝像頭拍照並保存照片。為了使用和後續開發方便,這裡利用pyqt5設計了個相機界面,後面將介紹如何實現,要點包括界面設計、邏輯實現及完整代碼。 ...
  • 思路分析 註冊頁面需要對用戶提交的數據進行校驗,並且需要對用戶輸入錯誤的地方進行提示! 所有我們需要使用forms組件搭建註冊頁面! 平時我們書寫form是組件的時候是在views.py裡面書寫的, 但是為了接耦合,我們需要將forms組件都單獨寫在一個地方,需要用的時候導入就行! 例如,在項目文件 ...
  • 思路分析 登錄頁面,我們還是採用ajax的方式提交用戶數據 唯一需要學習的是如何製作圖片驗證碼! 具體的登錄頁面效果圖如下: 如何製作圖片驗證碼 推導步驟1:在img標簽的src屬性里放上驗證碼的請求路徑 補充1.img的src屬性: 1.圖片路徑 2.url 3.圖片的二進位數據 補充2:字體樣式 ...
  • 哈嘍,兄弟們! 最近有許多小伙伴都在吐槽打工好難。 每天都是執行許多重覆的任務 例如閱讀新聞、發郵件、查看天氣、打開書簽、清理文件夾等等, 使用自動化腳本,就無需手動一次又一次地完成這些任務, 非常方便啊有木有?! 而在某種程度上,Python 就是自動化的代名詞。 今天就來和大家一起學習一下, 用 ...
  • 作者:IT王小二 博客:https://itwxe.com 前面小二介紹過使用Typora+PicGo+LskyPro打造舒適寫作環境,那時候需要使用水印功能,但是小二在升級LskyPro2.x版本發現有很多不如人意的東西,遂棄用LskyPro使用MinIO結合代碼實現自己需要的圖床功能,也適合以後 ...
  • OpenAI Gym是一款用於研發和比較強化學習演算法的工具包,本文主要介紹Gym模擬環境的功能和工具包的使用方法,並詳細介紹其中的經典控制問題中的倒立擺(CartPole-v0/1)問題。最後針對倒立擺問題如何建立控制模型並採用爬山演算法優化進行了介紹,並給出了相應的完整python代碼示例和解釋。要... ...
  • python爬蟲瀏覽器偽裝 #導入urllib.request模塊 import urllib.request #設置請求頭 headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, l ...
  • 前端代碼搭建 主要利用的是bootstrap3中js插件里的模態框版塊 <li><a href="" data-toggle="modal" data-target=".bs-example-modal-lg">修改密碼</a></li> <div class="modal fade bs-exam ...