常見軟體的GC演算法解析

2021-10-04 06:29:27 字數 2730 閱讀 1453

今天和大家一起解析下常見的gc演算法設計。

gc是一種軟體進行自動的記憶體**的方式。

如果軟體執行過程中,發現某些物件沒有了引用(或者稱之為不可達)的狀態時,就會啟動gc過程。將這部分記憶體進行釋放。以避免程式設計師因為忘記顯示釋放記憶體導致程式出現out of memory。

gc的過程主要分為標記、移動和壓縮

軟體先分析堆中的所有記憶體物件,判斷是否「存活」。

如果乙個物件沒有保持著被其他存活物件引用,就需要被清理

將標記為存活的物件移動到另乙個記憶體空間(老生代)

由於記憶體清理之後會出現很多碎片(非連續的小段可用記憶體),因此往往需要對其進行移動,確保大塊的記憶體可用空間。(有時候移動和壓縮會放在一起操作)

這裡我們先思考乙個問題,為什麼需要把存活的物件移動到另乙個記憶體空間。

首先,gc是乙個非常耗效能的過程。

因為在gc過程中,你的程式中各個物件的引用指向的記憶體位址可能發生改變。

如果此時你仍然在執行程式,就可能訪問到錯誤的記憶體位址。

因此gc期間會掛起程式的執行,也就是我們俗稱的stop the world。

在這乙個限制下,我們設計gc演算法的時候,必須做到盡可能少的進行gc

我們每次gc時,盡可能保證掃瞄的物件最大比例的釋放記憶體。這樣可用記憶體越多,觸發gc的次數就會越少。

ok,我們再引進乙個社會工程學知識,就是越新建立的物件,被清理的概率越高。

如果乙個物件經過了一次gc,仍然存活了下來,那麼它很有可能再下一次gc中存活。

那麼將其放在乙個單獨的記憶體空間(老生代)中,可以有效的減少這些長生命週期物件的gc次數。

而僅對這些新生成的物件(新生代)進行gc,可以使用更少的物件掃瞄,完成近似相當的記憶體釋放。

只有當老生代的記憶體空間也滿了,才會進行老生代的gc。

我們再思考一下,如果老生代也滿了,那麼gc時記憶體物件還能移動到**去呢?

在開乙個「老老生代」?那「老老生代」也滿了呢?

我們當然不能無限的開闢這麼多的記憶體空間,放置gc存活的物件,這樣記憶體的有效使用率太低了。

這裡我們會在原始的老生代記憶體空間直接移動物件,將那些被**的物件產生的記憶體碎片壓縮即可。

.net的gc設計很簡單,就同我們剛剛分析的情況基本一致。

它將記憶體分為三個區域,第0代,第1代,第2代。

所有的新物件的記憶體會分配至第0代。

當第0代滿了,觸發gc將第0代清空,將存活物件提公升至第1代。

當第1代滿了,再存活物件提公升至第2代。

當第2代滿了,gc後的存活物件直接在第2代的空間進行碎片壓縮。

在.net的gc設計中可能會出現這種情況,在第0代gc過程中,最新生成的記憶體物件被提公升到了第1代中。

但是這些物件很可能是一些短生命週期的物件,僅僅是「偶然」在gc之前生成罷了。

但是這些物件被提公升至第1代之後,很可能在之後的多次gc中,都不能被清理。

這些「無用」的記憶體占用就會消耗我們軟體的記憶體空間。

v8 採用了另一種設計思想。

v8僅有新生代和老生代兩個空間,並且它將新生代分成了from和to兩個半空間。

每次新物件直接生成在from半空間內。

當觸發gc時,from半空間內存活的物件被移動到to半空間內。

然後令to成為新的from半空間,from成為新的to半空間,即「半空間翻轉」

在下一次gc時,檢查gc存活物件,如果已經經歷了一次gc,那麼提公升至老生代,否則還是移動到to半空間。

在v8的這種設計下,物件必須經歷2次gc後才能提公升到老生代。

避免了因為生成時機問題,導致記憶體中的「生命週期」延長。

v8半空間的設計也會帶來的乙個問題就是,新生代的記憶體可用空間只有一半。

那麼有沒有什麼方式可以增加一些空間呢?

jvm設計gc時將新生代分成了三塊,eden,s0和s1(from倖存空間,to倖存空間)。

所有的物件建立時,記憶體分配至eden區域。

而當第一gc時存活物件提公升至s0區域(from倖存空間)。

再下一次gc時,會將eden和s0一起進行gc,並且將存活物件移動至s1區域(to倖存空間)。

然後和v8一樣s0和s1,發生「倖存空間翻轉」

僅當物件在倖存空間存活超過了8次,才提公升至老生代。

這種設計基於如下現實:每次gc僅有少部分物件存活。

因此jvm就將需要進行翻轉的半空間縮小了,這樣就能有更大的空間用於新物件記憶體分配。

這裡我們分析了.net,v8和jvm的gc設計,但是並不是說明那種軟體的gc演算法更好,所有的演算法架構都是進行一種設計取捨。

我們需要知道大部分情況下,gc能夠直接幫我們處理記憶體問題。

而在那些記憶體敏感的應用場景,期望依賴某一軟體的gc設計原生機制來自動處理,也是不會奏效的。

本文會經常更新,請閱讀個人部落格原文: ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。

說說JVM的GC功能之一GC演算法的選擇

如果你的應用可以忍受full gc帶來的停頓,throught收集器 即並行gc 能獲得最高的效能。同是他使用cpu和堆的大小都比其他的收集器少 當然不包括serial收集器,它的使用場景太有限 如果無法忍受full gc帶來的停頓,如果可用堆較小,可以選擇cms或g1,如果可用堆較大,建議使用g1...

常見集群 Cluster 軟體和技術解析

集群就是通過軟體將一組伺服器作為乙個整體向客戶提供資源。這些單個的伺服器就是集群的節點。當 對外提供資源的節點故障後 集群中其餘的節點能夠 將資源接管起來,繼續對客戶提供資源 集群技術的核心就是資源訪問控制。由於集群中所有節點都可以訪問集群對外共享的資源,當多個節點同時操作同乙個資源的時候,就可能引...

常見集群 Cluster 軟體和技術解析

集群就是通過軟體將一組伺服器作為乙個整體向客戶提供資源。這些單個的伺服器就是集群的節點。當對外提供資源的節點故障後,集群中其餘的節點能夠將資源接管起來,繼續對客戶提供資源。集群技術的核心就是資源訪問控制。由於集群中所有節點都可以訪問集群對外共享的資源,當多個節點同時操作同乙個資源的時候,就可能引發問...