Java垃圾回收机制(GC原理)解析
文章目錄
- 前言
- 一、為什么要GC
- 二、什么時候GC
- 三、判斷對象是否存活的方法
- 1.引用計數(shù)法
- 2.可達性分析法
- 3.可作為GCRoots的對象
- 四、垃圾回收算法
- 1.標記清除法
- 2.復(fù)制算法
- 3.標記整理法
- 4.分代回收算法
- 五、垃圾回收算器
前言
Java相比于c/c++一個最顯著的特征就是引入了垃圾回收機制,使我們不用像c/c++編寫時還要注意內(nèi)存管理,java中JVM替我們完成了這部分工作
一、為什么要GC
對于系統(tǒng)而言,內(nèi)存遲早都會被消耗完,因為不斷的分配內(nèi)存空間而不進行回溯,就好像不停的產(chǎn)生生活垃圾
但是除了釋放垃圾對象,也需要對于內(nèi)存空間進行碎片管理,沒有GC就不能保證應(yīng)用程序的正常化進行
二、什么時候GC
堆區(qū)分為老年代和新生代,新生代又分為Eden區(qū)、s0區(qū)和s1區(qū)(99%的對象能new到Eden區(qū),1%大對象new到老年代),當對象去堆區(qū)申請空間時
(1)先去Eden區(qū)看有無足夠空間,有分配,無mirror GC
(2)有分配,無去s區(qū),有Eden區(qū)對象移到s區(qū),無去old區(qū)
(3)有s移到old,Eden移到s,無full GC
(4)old區(qū)有同上,無OOM
三、判斷對象是否存活的方法
1.引用計數(shù)法
- 原理:對每一個對象保存一個整形的引用計數(shù)器屬性,用于記錄對象被引用的情況。
例:一個對象A只要有任何一個對象引用了A則A的引用計數(shù)器就+1,當引用失效時,引用計數(shù)器就-1.只要對象A的引用計數(shù)器的值為0,即標識對象A不可能再被使用,可進行回收
-
優(yōu)點:實現(xiàn)簡單,垃圾對象便于識別,判斷效率高
-
缺點:
他需要單獨的字段存儲計數(shù)器,這樣的做法增加的存儲空間的開銷
每次賦值需要額外的加減法計算,增加了時間開銷
引用計數(shù)算法最大的問題是無法處理循環(huán)引用的情況,這是一個比較致命的缺陷
2.可達性分析法
相對于引用計數(shù)算法,他有效的解決了在引用計數(shù)算法中的循環(huán)引用問題,防止內(nèi)存泄漏發(fā)生
這種類型的垃圾收集也叫作追蹤性垃圾收集
概念:
(1)可達性分析算法以跟對象集合為起點,按照從上至下的方式搜索被跟對象集合所鏈接的對象目標是否可達
(2)使用可達性分析算法后,內(nèi)存中的存貨對象會被跟對象集合直接或者間接連接著,搜索所走過的路徑稱之為引用鏈
(3)如果目標對象沒有任何陰影鏈項鏈,則是不可達的,意味著該對象已經(jīng)死亡,可以標記為垃圾對象。
在可達性分析算法中只有能夠被根對象集合直接或間接連接的對象才是存活對象。
3.可作為GCRoots的對象
- 虛擬機棧匯總的引用對象
例:各個線程被調(diào)用的方法中使用的參數(shù)、局部變量等 - 本地方法棧內(nèi)JNI引用的對象
- 方法區(qū)中類靜態(tài)屬性引用對象
例:JAVA類的引用類型靜態(tài)變量 - 方法區(qū)中常量引用的對象
例:字符串常量池里面的引用 - 所有被同步所synchronize持有的對象
- java虛擬機內(nèi)部引用的對象
例:基本數(shù)據(jù)類型對應(yīng)的Class對象,一些常駐的異常對象(NullPointerException等) - 系統(tǒng)類加載器
總結(jié):一個指針,他保存了堆里面的對象,但自己又不在堆當中,那么他就是一個Root
四、垃圾回收算法
1.標記清除法
-
背景:
標記清除算法是一種非常基礎(chǔ)和常見的垃圾收集算法,該算法被J.McCarthy等人在1960年提出并應(yīng)用于Lisp語言 -
執(zhí)行過程:
(1)當堆空間中有效內(nèi)存空間被耗盡時,就會停止這個程序(Stop the world),然后進行兩項工作,標記,清除這兩部分
(2)標記:從引用根節(jié)點上開始遍歷(可達性分析算法)標記所有被引用的對象。一般是在對象Header中記錄為可達對象。
(3)清除:對堆內(nèi)存從頭到尾進行線性遍歷,如果發(fā)現(xiàn)某個對象在其Header中沒有標記為可達對象,則將其回收 -
缺點:效率不高;在進行GC的時候需要停止整個應(yīng)用程序,導(dǎo)致用戶體驗差;且會產(chǎn)生的大量的內(nèi)存碎片
-
注意:
在這里的清除不是去干掉具體內(nèi)存中的數(shù)據(jù),而是本身分配的是一組連續(xù)的內(nèi)存編碼給我們使用,清除就是在回收這些空閑地址,將他們保存在空閑地址表當中,下次有心得對象需要空間時去判斷是否夠用
2.復(fù)制算法
-
背景:
為了解決標記-清除算法在垃圾收集效率方面的缺陷,M.LMinsky與1963年發(fā)表了著名論文,”使用雙存儲區(qū)的Lisp語言垃圾收集器“,該論文中被描述的算法被人們稱之為復(fù)制算法。 -
執(zhí)行過程:
將內(nèi)存空間分為兩塊,每次只使用其中一塊,在垃圾回收的時候,將正在使用的內(nèi)存中的存活對象復(fù)制到未被使用的內(nèi)存塊中,之后清除正在使用的內(nèi)存塊,交換兩個內(nèi)存角色。 -
缺點:
1.需要兩倍空間
2.GC需要維護對象的引用關(guān)系,時間開銷加大
此種方案使用與垃圾對象較少,量級不大的情況
3.標記整理法
-
背景:
復(fù)制算法的高效是簡歷在存貨對象少、垃圾對象多的前提下。這種情況在新生代中經(jīng)常法神,但是在老年代,更常見的情況是大部分對象都是存貨的。如果依然使用復(fù)制算法,由于存貨對象多,復(fù)制成本也會非常高。因此基于老年代使用復(fù)制算法并不適用。 -
執(zhí)行過程:
第一階段與標記清除算法一致。
第二階段將所有的存貨對象壓縮到內(nèi)存的一段,按照順訊排放,之后清理邊界外所有空間
4.分代回收算法
-
背景:為了滿足垃圾回收的效率最優(yōu)性,所以分代手機算法應(yīng)運而生。
分代手機算法基于一個事實:不同的對象生命周期是不一樣的,因此,不同生命周期的對象可以采取不同的手機方式,以便于提高回收效率。一般是把JAVA堆分為新生代和老年代,這樣就可以根據(jù)各個年代的特點使用不同回收算法,相對提高效率
在系統(tǒng)運行過程匯總,會產(chǎn)生大量對象,其中有些對象是業(yè)務(wù)信息相關(guān),如HTTP請求的Session、線程、Socket連接等對象,這類對象跟業(yè)務(wù)掛鉤,因此生命周期長,還有一部分是運行過程匯總生成的臨時變量,這些對象生命周期短,比如:String,這些對象甚至只使用一次即可回收 -
目前所有GC都采用分代收集算法進行執(zhí)行
對象的狀態(tài)經(jīng)過大量的調(diào)研研究劃分為年青代與老年代兩個類別
(1)年輕代:區(qū)域相對小,對象生命周期短、存活率低,且產(chǎn)生應(yīng)用頻繁
復(fù)制算法回收整理速度是最快的。復(fù)制算法效率只與當前存活對象大小有關(guān),因此很實用與年青代的回收,而空間問題,因為存活率問題,所以單獨開辟S0,S1兩塊空間處理清除后結(jié)果
(2)老年代:區(qū)域較大,生命周期長、存活率高,回收不及年青代頻繁
這種情況存在大量存過對象下,復(fù)制不適用,所以一般是用清除與整理算法混合實現(xiàn)
Mark階段的開銷與存活對象的數(shù)量成正比
Sweep階段的開銷與所管理的大小成正比
Compact階段的開銷與存活對象的數(shù)據(jù)成正比
五、垃圾回收算器
-
serial:針對新生代,jdk1.3之前,單線程,復(fù)制算法,垃圾回收會stop the world(停止用戶代碼執(zhí)行)
-
serial old:針對老年代,jdk1.3之前,標記整理
-
parNew,parallel Scavenge:新生代,多線程
-
parallel:老年代
-
G1:jdk1.7之后,新生代/老年代,可預(yù)測停頓(提供最優(yōu)的停頓時間),空間整理(提供最大的吞吐量)
-
CMS:jdk1.7之后,老年代 使用空閑列表回收,不對老年代進行整理
-
垃圾回收算器底層算法:
-
常用GC垃圾回收器性能對比
總結(jié)
以上是生活随笔為你收集整理的Java垃圾回收机制(GC原理)解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MOSS 2007基础:内容类型(Con
- 下一篇: JavaWeb中验证码的实现