[JVM]垃圾回收

Duancf發表於2024-07-12

垃圾回收機制
標記+清除

先標記哪些記憶體沒有被引用,然後釋放這些記憶體
注意,釋放不代表要重寫這些記憶體裡的資料,只需要把這段記憶體的起始和結束的地址記錄下來即可。
速度很快,但是很容易產生記憶體碎片

標記+整理

先標記哪些記憶體沒有被引用,然後釋放這些記憶體,
注意,釋放記憶體之後要進行緊湊操作,也就是要把仍然有效的資料挨在一起,
不容易產生記憶體碎片,但是,花費時間較高,因為要牽扯到記憶體資料的複製還有仍然有效的這些物件地址的修改

標記+複製

from to
0 1 1 1 0 0 0 0 0 0
我們對from區的資料進行標記,把仍然有效的資料都複製到to區

from to
0 0 0 0 0 1 1 1 0 0
最後,交換from和to區域的指標,把這兩塊區域換過來
保持to區一直是空閒的。

from to
1 1 1 0 0 0 0 0 0 0
不會產生記憶體碎片,但是需要佔用兩倍的記憶體空間

分代垃圾回收

將記憶體區域分為新生代和老年代,
新生代的區域存放那些使用過後就被回收的物件,
而老年代則存放那些需要一直儲存的物件,

新生代 老年代
伊甸園 from to
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
新產生的物件分配在伊甸園區域,
當伊甸園區域記憶體不夠時,進行minorgc垃圾回收,
把仍然有效的物件放入to區,給這些物件的壽命+1,然後交換from和to,保持to一直是空的狀態,
minorgc之後,伊甸園騰出空間,我們可以繼續往裡面存放物件,當伊甸園空間又不足的時候,我們再次進行minorgc,這一次不僅要考慮伊甸園中的物件是否存活,還要考慮from中的物件是否存活,把存活的仍然存活的物件放入to區,對其壽命+1,

當經過多次回收的物件仍然存活時,也就是from區的物件壽命超過一定值的時候(預設15,因為使用4bit儲存這個壽命),我們考慮將其放入老年代,

當老年代的記憶體也滿了的時候,先進行一次minor gc如果記憶體還不足,再進行fullgc,對新生代和老年代進行統一的清理。

minor gc和full gc 會引發stop the world,也就是會停下使用者執行緒,只允許垃圾回收的執行緒執行,因為垃圾回收涉及到了物件的移動操作,如果不停下使用者執行緒,使用者執行緒可能會去訪問一個已經被清空的地址,產生混亂。等垃圾回收結束,使用者執行緒再次執行。minor gc這個暫停時間是很短的,畢竟大多數的新生代都是垃圾,不需要移動很多。full gc暫停時間較長。

如果我們需要存放一個大物件,伊甸園沒有這麼大的空間而老年代卻可以放得下時,會直接放入老年代。

如果在一個執行緒中發生了記憶體溢位,並不會影響到主執行緒main的執行。

垃圾回收器

序列
單執行緒,堆記憶體較小,適合個人電腦
吞吐量優先
多執行緒,堆記憶體較大,多核cpu,在一定時間範圍內讓STW最短
響應時間優先
多執行緒,堆記憶體較大,多核CPU,只關注單次STW,讓每次STW最短

相關文章