java內存垃圾回收模型

一.java的內存模型

這裏寫圖片描述

介紹以下6個組成部分

1.程序計數器:一塊較小內存區域,指向當前所執行的字節碼。若是線程正在執行一個Java方法,這個計數器記錄正在執行的虛擬機字節碼指令的地址,若是執行的是Native方法,這個計算器值爲空。javascript

2.Java虛擬機棧:線程私有的,其生命週期和線程一致,每一個方法執行時都會建立一個棧幀用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。html

3.本地方法棧:與虛擬機棧功能相似,只不過虛擬機棧爲虛擬機執行Java方法服務,而本地方法棧則爲使用到的Native方法服務。java

4.Java堆:是虛擬機管理內存中最大的一塊,被全部線程共享,該區域用於存放對象實例,幾乎全部的對象都在該區域分配。Java堆是內存回收的主要區域,從內存回收角度看,因爲如今的收集器大都採用分代收集算法,因此Java堆還能夠細分爲:新生代和老年代,再細分一點的話能夠分爲Eden空間、From Survivor空間、To Survivor空間等。根據Java虛擬機規範規定,Java堆能夠處於物理上不連續的空間,只要邏輯上是連續的就行。算法

5.方法區:與Java同樣,是各個線程所共享的,用於存儲已被虛擬機加載類信息、常亮、靜態變量、即時編譯器編譯後的代碼等數據。markdown

6.運行時常量池,運行時常量池是方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期生成的各類字面量和符號引用。運行期間能夠將新的常量放入常量池中,用得比較多的就是String類的intern()方法,當一個String實例調用intern時,Java查找常量池中是否有相同的Unicode的字符串常量,如有,則返回其引用;若沒有,則在常量池中增長一個Unicode等於該實例字符串並返回它的引用。app

二.jvm內存模型

這裏寫圖片描述
普遍地說,JVM堆內存被分爲兩部分——年輕代(Young Generation)和老年代(Old Generation)。jvm

年輕代

年輕代是全部新對象產生的地方。當年輕代內存空間被用完時,就會觸發垃圾回收。這個垃圾回收叫作Minor GC。年輕代被分爲3個部分——Enden區和兩個Survivor區。ide

年輕代空間的要點:

大多數新建的對象都位於Eden區。
當Eden區被對象填滿時,就會執行Minor GC。並把全部存活下來的對象轉移到其中一個survivor區。
Minor GC一樣會檢查存活下來的對象,並把它們轉移到另外一個survivor區。這樣在一段時間內,總會有一個空的survivor區。
通過屢次GC週期後,仍然存活下來的對象會被轉移到年老代內存空間。一般這是在年輕代有資格提高到年老代前經過設定年齡閾值來完成的。this

年老代

年老代內存裏包含了長期存活的對象和通過屢次Minor GC後依然存活下來的對象。一般會在老年代內存被佔滿時進行垃圾回收。老年代的垃圾收集叫作Major GC。Major GC會花費更多的時間。線程

Stop the World事件

全部的垃圾收集都是「Stop the World」事件,由於全部的應用線程都會停下來直到操做完成(因此叫「Stop the World」)。

由於年輕代裏的對象都是一些臨時(short-lived )對象,執行Minor GC很是快,因此應用不會受到(「Stop the World」)影響。

三.java堆內存開關

這裏寫圖片描述

四.垃圾對象如何肯定

  Java堆中存放着幾所全部的對象實例,垃圾收集器在對堆進行回收前,首先須要肯定哪些對象還」活着」,哪些已經」死亡」,也就是不會被任何途徑使用的對象。

引用計數法

  引用計數法實現簡單,效率較高,在大部分狀況下是一個不錯的算法。其原理是:給對象添加一個引用計數器,每當有一個地方引用該對象時,計數器加1,當引用失效時,計數器減1,當計數器值爲0時表示該對象再也不被使用。須要注意的是:引用計數法很難解決對象之間相互循環引用的問題,主流Java虛擬機沒有選用引用計數法來管理內存。

可達性分析算法

  這個算法的基本思路就是經過一系列的稱爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來講,就是從GC Roots到這個對象不可達)時,則證實此對象是不可用的。如圖所示,對象object 五、object 六、object 7雖然互相有關聯,可是它們到GC Roots是不可達的,因此它們將會被斷定爲是可回收的對象。
  這裏寫圖片描述

在Java語言中,可做爲GC Roots的對象包括下面幾種:

虛擬機棧(棧幀中的本地變量表)中引用的對象。
方法區中類靜態屬性引用的對象。
方法區中常量引用的對象。
本地方法棧中JNI(即通常說的Native方法)引用的對象。

對象生存仍是死亡

  即便在可達性分析算法中不可達的對象,也並不是是「非死不可」的,這時候它們暫時處於「緩刑」階段,要真正宣告一個對象死亡,至少要經歷兩次標記過程:若是對象在進行可達性分析後發現沒有與GC Roots相鏈接的引用鏈,那它將會被第一次標記而且進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過,虛擬機將這兩種狀況都視爲「沒有必要執行」。程序中能夠經過覆蓋finalize()來一場」驚心動魄」的自我拯救過程,可是,這隻有一次機會。

引用

不管是經過引用計數算法判斷對象的引用數量,仍是經過可達性分析算法判斷對象的引用鏈是否可達,斷定對象是否存活都與「引用」有關。
在JDK 1.2之前,Java中的引用的定義很傳統:若是reference類型的數據中存儲的數值表明的是另一塊內存的起始地址,就稱這塊內存表明着一個引用。

在JDK 1.2以後,Java對引用的概念進行了擴充

將引用分爲強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種,這4種引用強度依次逐漸減弱。

強引用

就是指在程序代碼之中廣泛存在的,相似「Object obj = new Object()」這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。

軟引用

是用來描述一些還有用但並不是必需的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中進行第二次回收。若是此次回收尚未足夠的內存,纔會拋出內存溢出異常。在JDK 1.2以後,提供了SoftReference類來實現軟引用。

弱引用

也是用來描述非必需對象的,可是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。在JDK 1.2以後,提供了WeakReference類來實現弱引用。

虛引用

也稱爲幽靈引用或者幻影引用,它是最弱的一種引用關係。一個對象是否有虛引用的存在,徹底不會對其生存時間構成影響,也沒法經過虛引用來取得一個對象實例。爲一個對象設置虛引用關聯的惟一目的就是能在這個對象被收集器回收時收到一個系統通知。在JDK 1.2以後,提供了PhantomReference類來實現虛引用。

五.典型的垃圾回收算法

1.Mark-Sweep(標記-清除)算法

  這是最基礎的垃圾回收算法,之因此說它是最基礎的是由於它最容易實現,思想也是最簡單的。標記-清除算法分爲兩個階段:標記階段和清除階段。標記階段的任務是標記出全部須要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。具體過程以下圖所示:

這裏寫圖片描述
  從圖中能夠很容易看出標記-清除算法實現起來比較容易,可是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會致使後續過程當中須要爲大對象分配空間時沒法找到足夠的空間而提早觸發新的一次垃圾收集動做。

2.Copying(複製)算法

  爲了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來。它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。具體過程以下圖所示:
這裏寫圖片描述

這種算法雖然實現簡單,運行高效且不容易產生內存碎片,可是卻對內存空間的使用作出了高昂的代價,由於可以使用的內存縮減到原來的一半。

很顯然,Copying算法的效率跟存活對象的數目多少有很大的關係,若是存活對象不少,那麼Copying算法的效率將會大大下降。

3.Mark-Compact(標記-整理)算法

  爲了解決Copying算法的缺陷,充分利用內存空間,提出了Mark-Compact算法。該算法標記階段和Mark-Sweep同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存。具體過程以下圖所示:

這裏寫圖片描述

4.Generational Collection(分代收集)算法

  分代收集算法是目前大部分JVM的垃圾收集器採用的算法。它的核心思想是根據對象存活的生命週期將內存劃分爲若干個不一樣的區域。通常狀況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集時只有少許對象須要被回收,而新生代的特色是每次垃圾回收時都有大量的對象須要被回收,那麼就能夠根據不一樣代的特色採起最適合的收集算法。

  目前大部分垃圾收集器對於新生代都採起Copying算法,由於新生代中每次垃圾回收都要回收大部分對象,也就是說須要複製的操做次數較少,可是實際中並非按照1:1的比例來劃分新生代的空間的,通常來講是將新生代劃分爲一塊較大的Eden空間和兩塊較小的Survivor空間(通常爲8:1:1),每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象複製到另外一塊Survivor空間中,而後清理掉Eden和剛纔使用過的Survivor空間。

  而因爲老年代的特色是每次回收都只回收少許對象,通常使用的是Mark-Compact算法。

引用

來自以下兩篇博客的總結
http://www.cnblogs.com/luoxn28/p/5492840.html
http://www.importnew.com/14086.html

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
相關文章
相關標籤/搜索