天天看點

JVM記憶體管理算法

1.判斷對象是否存活

引用計數法

很多教科書判斷對象是否存活的算法是這樣昨給對象中添加一個引用計數器,每當有 一個地方引用它時,計數器值就加1:當引用失效時,計數器值就減1;任何時刻計數器為 0的對象就是不可能再被使用的。

客觀地說,引用計數算法(Reference Counting)的實作簡單,判定效率也很高,在大部分情況下它都是一個不錯的算法.也有一些比較著名的應用案例,例如微軟公司的COM(Component Object Model)技術、使用Action5cript 3的FIashPlayer.Python語言和在遊戲腳本領域被廣泛應用的Squirzel中都使用了引用計數算法進行内在管。但是,至少主流的Java虛拟機裡面沒有選用引用計數算法來管理記憶體,其中最主要原因是它很難解決對象之間互循環引用的問題。

JVM記憶體管理算法

可達性分析算法

這個算法的基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鍊(Reference Chain),當一個對象到GC Roots沒有任何引用鍊相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。如下圖所示,對象object 5、object 6、object 7雖然互相有關聯,但是它們到GC Roots是不可達的,是以它們将會被判定為是可回收的對象。

JVM記憶體管理算法

在java鐘隻有以下的對象才可以被作為GC Root. 1>虛拟機棧(棧幀中的本地方法表)中引用的對象。2>方法區中類靜态屬性引用的對象。

3>方法區中常量引用的對象。

4>本地方法棧JNI(即一般說的Native方法)的引用對象。

2.垃圾回收算法

JVM規範中并沒有明确GC的運作方式,各個廠商可以采用不同的方式去實作垃圾回收器。這裡讨論幾種常見的GC算法。

标記-清除算法(Mark-Sweep)

最基礎的垃圾回收算法,分為兩個階段,标注和清除。标記階段标記出所有需要回收的對象,清除階段回收被标記

的對象所占用的空間。如圖:

JVM記憶體管理算法

從圖中我們就可以發現,該算法最大的問題是記憶體碎片化嚴重,後續可能發生大對象不能找到可利用空間的問題。

複制算法(Copying)

為了解決Mark-Sweep算法記憶體碎片化的缺陷而被提出的算法。按記憶體容量将記憶體劃分為等大小的兩塊。每次隻使用其中一塊,當這一塊記憶體滿後将尚存活的對象複制到另一塊上去,把已使用的記憶體清掉,如圖:

JVM記憶體管理算法

這種算法雖然實作簡單,記憶體效率高,不易産生碎片,但是最大的問題是可用記憶體被壓縮到了原本的一半。且存活

對象增多的話,Copying算法的效率會大大降低。

标記-整理算法(Mark-Compact)

結合了以上兩個算法,為了避免缺陷而提出。标記階段和Mark-Sweep算法相同,标記後不是清理對象,而是将存活對象移向記憶體的一端。然後清除端邊界外的對象。如圖:

JVM記憶體管理算法

分代收集算法(Generational Collection)

分代收集法是目前大部分JVM所采用的方法,其核心思想是根據對象存活的不同生命周期将記憶體劃分為不同的域,一般情況下将GC堆劃分為老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特點是每次垃圾回收時隻有少量對象需要被回收,新生代的特點是每次垃圾回收時都有大量垃圾需要被回收,是以可以根據不同區域選擇不同的算法。

目前大部分JVM的GC對于新生代都采取Copying算法,因為新生代中每次垃圾回收都要回收大部分對象,即要複制的操作比較少,但通常并不是按照1:1來劃分新生代。一般将新生代劃分為一塊較大的Eden空間和兩個較小的Survivor空間(From Space, To Space),每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,将該兩塊空間中還存活的對象複制到另一塊Survivor空間中。

JVM記憶體管理算法
JVM記憶體管理算法

而老生代因為每次隻回收少量對象,因而采用Mark-Compact算法。

另外,不要忘記在Java基礎:Java虛拟機(JVM)中提到過的處于方法區的永生代(Permanet Generation)。它用來存儲class類,常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類。

對象的記憶體配置設定主要在新生代的Eden Space和Survivor Space的From Space(Survivor目前存放對象的那一塊),少數情況會直接配置設定到老生代。當新生代的Eden Space和From Space空間不足時就會發生一次GC,進行GC後,Eden Space和From Space區的存活對象會被挪到To Space,然後将Eden Space和From Space進行清理。如果To Space無法足夠存儲某個對象,則将這個對象存儲到老生代。在進行GC後,使用的便是Eden Space和To Space

了,如此反複循環。當對象在Survivor區躲過一次GC後,其年齡就會+1。預設情況下年齡到達15的對象會被移到老生代中。