網站首頁 編程語言 正文
目錄
- 檢測垃圾
- 引用計數算法
- 可達性分析算法
- 回收垃圾
- 標記清除算法
- 復制算法
- 標記整理算法
- 分代算法
Java運行時內存中的程序計數器、虛擬機棧、本地方法棧這三部分區域其生命周期與相關線程有關,隨線程而生,隨線程而滅。而程序計數器就是一個單純存地址的整數也不需要關心,因此我們GC(垃圾回收)的主要目標就是堆(堆中存放著幾乎所有實例對象)!
檢測垃圾
一個對象,如果后續不再被使用且沒有引用指向它,就可以認為是垃圾。
有以下方法可知對象是否有引用指向:
引用計數算法
在主流的JVM中沒有選用引用計數法來管理內存,最主要的原因就是引用計數法無法解決對象的循環引用問題 Python,PHP采取了引用計數算法。
給對象增加一個引用計數器,每當有一個地方引用它時,計數器就+1;當引用失效時,計數器就-1;任何時刻計數器為0的對象就是不能再被使用的,即可被回收。
Test test1 = new Test();
Test test2 = test1;
Test test3 = test1;
Test test4 = test1;
此算法存在兩個缺陷:
- 浪費內存空間
- 存在循環引用的情況
用一個例子來解釋一下循環引用的問題:
class Test {
public Test test;
}
Test test1 = new Test();
Test test2 = new Test();
test1.test=test2;
test2.test=test1;
此時test1與test2 銷毀了,兩個對象的引用計數分別減一。
此時這兩個對象的引用計數不為0,不能作為垃圾且無法使用,陷入了一個邏輯上的循環。
可達性分析算法
通過一系列稱為"GC Roots"的對象作為起始點,從這些節點開始向下搜索,搜索走過的路徑稱之為"引用鏈",當一個對象到GC Roots沒有任何的引用鏈相連時(從GC Roots到這個對象不可達)時,證明此對象是不可用的。
在java中,可作為GC Roots(垃圾回收根對象)的對象有以下幾種:
- 棧上的局部變量
- 常量池中引用的對象
- 方法區中類靜態屬性引用的對象
只有從GC Roots對象開始,通過引用鏈可達的對象才被認為是存活的,而無法通過引用鏈訪問的對象則會被判定為垃圾,并進行回收。
缺點:
- 遍歷開銷:可達性分析算法需要遍歷整個對象圖以確定每個對象的可達性。對于大型堆和復雜的引用關系,遍歷開銷可能非常大,特別是在全局垃圾回收中。這可能會導致垃圾回收的性能下降。
- 延遲回收:可達性分析算法需要遍歷整個對象圖,從GC Roots開始,逐個檢查每個對象的引用關系。這個過程可能需要消耗大量的時間,且在垃圾回收期間,應用程序的執行會被暫停(STW - Stop-The-World)。因此,可達性分析算法可能導致較高的延遲,影響程序的響應性能。
回收垃圾
標記清除算法
算法分為"標記"和"清除"兩個階段 : 首先標記出所有需要回收的對象,在標記完成后統一回收所有被標記的對象。
回收前:
回收后:
不足:
- 標記和清除這兩個過程的效率都不高。
- 標記清除后會產生大量不連續的內存碎片,空間碎片太多可能會導致以后在程序運行中需要分配較大對象時,無法找到足夠連續內存而不得不提前觸發另一次垃圾收集。
復制算法
"復制"算法是為了解決"標記-清理"的效率問題。它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。
簡單來說復制算法就是把不是垃圾的對象拷貝到未使用的那一邊,然后再統一釋放剛才使用過的那一塊區域。
缺點:
- 內存利用率比較低。
- 如果當前區域的大部分對象都需要保留垃圾很少,那么此時的復制成本就比較高。
標記整理算法
標記整理算法的基本流程:
-
標記:從根對象開始,通過可達性分析算法,標記所有從根對象可達的存活對象。標記過程中,通常使用標記位(標記狀態)來標記對象是否為存活對象。
-
整理:將所有存活對象移到堆的一端,緊湊排列,以便釋放出連續的一段內存空間。在移動存活對象時,需要更新對這些對象的引用,確保引用指向移動后的新位置。
-
更新引用:在堆中,遍歷所有存活對象的引用,將其指向新的位置。這是為了避免懸掛指針(引用指向被移動或已回收的對象)的問題。
-
釋放未標記的對象:在整理后的堆中,所有沒有被標記的對象都可以被認為是垃圾對象,可以直接被回收。
優點:解決了內存碎片問題。
缺點:搬運復制開銷比較大。
分代算法
分代算法主要基于一種觀察:大部分對象的生命周期都比較短暫。根據這個觀察,分代算法將堆內存劃分為不同的代(Generation),每一代中對象的生命周期不同,并且根據對象的生命周期將不同的垃圾回收策略應用于不同的代中。
一般來說,分代堆內存被劃分為年輕代(Young Generation)和老年代(Old Generation)兩個主要部分
年輕代:
年輕代是存放新創建的對象的地方,大部分對象在創建后很快就變為垃圾對象。年輕代通常進一步分為Eden區和兩個Survivor區。新創建的對象首先放入Eden區,當Eden區滿時,不會被回收的對象會被轉移到一個Survivor區。當一個Survivor區滿時,其中的存活對象會被復制到另一個Survivor區或者老年代。經過多次垃圾回收后依然存活的對象將晉升到老年代。
年輕代通常采用復制算法(Copying)作為垃圾回收策略,因為新創建的對象的生命周期短暫,復制算法可以更好地利用對象的特點。
老年代:
老年代是存放存活時間較長的對象的地方,老年代的對象生命周期較長,垃圾回收頻率相對較低。對于老年代的垃圾回收,可以采用標記-清除(Mark-Sweep)或者標記-整理(Mark-Compact)等算法。
分代算法通過區分不同代的對象,針對不同代采取不同的垃圾回收策略,可以提高垃圾回收效率和系統性能。年輕代的頻繁垃圾回收可以快速回收新創建對象,老年代的較少回收可以減少全局垃圾回收的引發,提高應用程序的響應性。這種分代垃圾回收策略在大多數的垃圾收集器中都有應用。
原文鏈接:https://blog.csdn.net/st200112266/article/details/131728218
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-05-21 Nginx實現會話保持的兩種方式_nginx
- 2022-10-13 Android自定義View實現簡單水波紋效果_Android
- 2023-03-17 redis中hash數據結構及說明_Redis
- 2022-09-18 Golang實現文件傳輸功能_Golang
- 2022-10-21 React?模式之純組件使用示例詳解_React
- 2022-04-24 教你使用mongoose實現多集合關聯查詢_MongoDB
- 2022-06-30 深度卷積神經網絡各種改進結構塊匯總_其它綜合
- 2022-05-26 Flutter實現滑動塊驗證碼功能_Android
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支