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