Java 垃圾回收機制 (分代垃圾回收ZGC)


什么是自動垃圾回收?

自動垃圾回收是一種在堆內存中找出哪些對象在被使用,還有哪些對象沒被使用,並且將后者刪掉的機制。所謂使用中的對象(已引用對象),指的是程序中有指針指向的對象;而未使用中的對象(未引用對象),則沒有被任何指針給指向,因此占用的內存也可以被回收掉。

在用 C 之類的編程語言時,程序員需要自己手動分配和釋放內存。而 Java 不一樣,它有垃圾回收器,釋放內存由回收器負責。本文接下來將介紹垃圾回收機制的基本過程。

 

第一步:標記

這個過程的第一步叫做標記。這一階段,垃圾回收器識別哪些內存正在使用,哪些內存未被使用。

被引用的對象以藍色展示。未被引用的對象以金黃色展示。在標記階段,所有對象都需要掃描並做出判斷。如果系統中的所有對象都必須掃描到,這將是一個非常耗時的過程。

第二步:清除

這一步會刪掉標記出的未引用對象。

內存分配器會保留指向可用內存的引用,以供分配新對象。

壓縮

為了提升性能,刪除了未引用對象后,還可以將剩下的已引用對象放在一起(壓縮),這樣就能更簡單快捷地分配新對象了。

為什么使用分代垃圾回收?

如前所述,標記並整理所有JVM里的對象,效率是很低的。隨着越來越多的對象被分配,對象列表不斷增長,導致垃圾回收時間越來越長。然而,根據應用程序的經驗分析,大多數的對象都是短命的。

下面是這類數據的一個例子。Y軸表示分配的字節數,X軸表示隨着時間分配的字節數。

正如你所看到的,隨着時間的推移,仍然存活的對象越來越少。事實上,大多數對象的生命周期都很短,如圖左側較高的值所示。

 

JVM 分代

根據之前的規律,就可以用來提升 JVM 的效率了。方法是,把堆分成幾個部分(就是所謂的分代),分別是新生代、老年代,以及永生代。

新對象會被分配在新生代內存。一旦新生代內存滿了,就會開始對死掉的對象,進行所謂的小型垃圾回收過程。一片新生代內存里,死掉的越多,回收過程就越快;至於那些還活着的對象,此時就會老化,並最終老到進入老年代內存。

Stop the World 事件 —— 小型垃圾回收屬於一種叫 "Stop the World" 的事件。在這種事件發生時,所有的程序線程都要暫停,直到事件完成(比如這里就是完成了所有回收工作)為止。

 

老年代用來保存長時間存活的對象。通常,設置一個閾值,當達到該年齡時,年輕代對象會被移動到老年代。最終老年代也會被回收。這個事件成為 Major GC。

Major GC 也會觸發STW(Stop the World)。通常,Major GC會慢很多,因為它涉及到所有存活對象。所以,對於響應性的應用程序,應該盡量避免Major GC。還要注意,Major GC的STW的時長受年老代垃圾回收器類型的影響。

永久代包含JVM用於描述應用程序中類和方法的元數據。永久代是由JVM在運行時根據應用程序使用的類來填充的。此外,Java SE類庫和方法也存儲在這里。

如果JVM發現某些類不再需要,並且其他類可能需要空間,則這些類可能會被回收。

 

分代垃圾回收過程

既然已經了解了為什么將堆分成不同的代,現在就該看看這些空間到底是如何交互的。下面的圖片展示了JVM中對象分配和老化的過程。

首先,任何新對象都在Eden區分配。兩個survivor區都是空的。

當Eden區滿了,觸發一次Minor GC。

被引用的對象被移動到第一個Survivor區,未被引用的對象會在Eden區清除時一並刪除。

 

在下一次Minor GC中,Eden區也會做同樣的操作。刪除未被引用的對象,並將被引用的對象移動到Survivor區。然而,這里,他們被移動到了第二個Survivor區(S1)。此外,第一個Survivor區(S0)中,在上一次Minor GC幸存的對象,會增加年齡,並被移動到S1中。待所有幸存對象都被移動到S1后,S0和Eden區都會被清空。注意,Survivor區中有了不同年齡的對象。

在下一次Minor GC中,會重復同樣的操作。不過,這一次Survivor區會交換。被引用的對象移動到S0,。幸存的對象增加年齡。Eden區和S1被清空。

 

此幻燈片演示了 promotion。 在較小的GC之后,當老化的物體達到一定的年齡閾值(在該示例中為8)時,它們從年輕一代晉升到老一代。

隨着較小的GC持續發生,物體將繼續被推廣到老一代空間。

所以這幾乎涵蓋了年輕一代的整個過程。 最終,將主要對老一代進行GC,清理並最終壓縮該空間。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM