jvm垃圾回收機制

     jvm的垃圾回收就是對jvm中不用的對象進行釋放,將其所佔的內存進行回收。

首先介紹下jvm的內存,jvm的內存結構如下圖所示:


JVM 內存包含如下幾個部分:

  • Heap(堆) Memory 存放Java對象

  • Non-Heap Memory 存放類加載信息和其它meta-data

  • Other 存放JVM 自身代碼等

    JVM啓動時,就已經保留了固定的內存空間給Heap內存,這部分內存並不一定都會被JVM使用,但是可以確定的是這部分保留的內存不會被其他進程使用。這部分內存大小由 -Xmx參數指定。而另一部分內存在JVM啓動時就分配給JVM,作爲JVM的初始Heap內存使用。影響這個的參數是 -Xms

默認空餘堆內存小於40%時,JVM 就會增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio指定。
默認空餘堆內存大於70%時,JVM 會減少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio指定。

可以通過-XX:MaxPermSize設置Non-Heap大小.

GC 的年代劃分

wKioL1TYGaHSEzyIAAD5qrDjIE8247.jpg

Java堆中各代分佈

Young:主要是用來存放新生的對象。

Old:主要存放應用程序中生命週期長的內存對象。

Permanent:是指內存的永久保存區域,主要存放ClassMeta的信息,Class在被 Load的時候被放入PermGen space區域. 它和存放InstanceHeap區域不同,GC(Garbage Collection)不會在主程序運行期對PermGen space進行清理,所以如果你的APPLOAD很多CLASS的話,就很可能出現PermGen space錯誤。

wKioL1TYGcWxgwilAADQA7xoTy4631.jpg

    -Xms指定的值比-Xmx的小,那麼兩者的差值就是Virtual內存值。隨着程序的運行,Eden區、 Tenured區和Perm區會逐漸使用保留的Virtual空間。

   JVM內存模型中Heap區分兩大塊,一塊是 NEW Generation,另一塊是Old Generation. 在NewGeneration中,有一個叫Eden的空間,主要是用來存放新生的對象,還有兩個Survivor Spaces(from,to),它們的大小總是一樣,它們用來存放每次垃圾回收後存活下來的對象。在OldGeneration中,主要存放應用程序中生命週期長的內存對象。在NewGeneration塊中,垃圾回收一般用Copying的算法,速度快。每次GC的時候,存活下來的對象首先由Eden 拷貝到某個Survivor Space, 當Survivor Space空間滿了後, 剩下的live對象就被直接拷貝到OldGeneration中去。因此,每次GC後,Eden內存塊會被清空。在OldGeneration塊中,垃圾回收一般用mark-compact的算法,速度慢些,但減少內存要求.

2.JVM 使用的GC算法是什麼?

分代收集。

即將內存分爲幾個區域,將不同生命週期的對象放在不同區域裏;

GC收集的時候,頻繁收集生命週期短的區域(Young area)

比較少的收集生命週期比較長的區域(Old area)

基本不收集的永久區(Perm area)

還有個Permanent Generation,主要用來放JVM自己的反射對象,比如類對象和方法對象等。關於這個區,它還提供String pool,看下面的例子:

String first = "abc";   
String second = new String ("abc");


第一個對象存貯在Permanent Generation,而第二個對象存儲在Heap裏面。所以:

String s = "abc";  
String p = "abc";


對象s和p指向同一個對象,這樣效率大大提高。

JVM垃圾回收簡介:

    垃圾回收分多級,0級爲全部(Full)的垃圾回收,會回收OLD段中的垃圾;1級或以上爲部分垃圾回收,只會回收NEW中的垃圾,內存溢出通常發生於OLD段或Perm段垃圾回收後,仍然無內存空間容納新的Java對象的情況。

3.GC Full GC 有什麼區別?

GC(或Minor GC):收集生命週期短的區域(Young area)

Full GC (或Major GC):收集生命週期短的區域(Young area)和生命週期比較長的區域(Old area)

他們的收集算法不同,所以使用的時間也不同。 GC 效率也會比較高,我們要儘量減少 Full GC 的次數。當顯示調用System.gc() 時,gc does a full collection(both young generation and tenured generation).

 

4.Minor GC後,Eden是空的嗎?

是的,Minor GC會把Eden中的所有活的對象都移到Survivor區域中,如果Survivor區中放不下,那麼剩下的活的對象就被移到Old generation 中。

8. 常見的內存泄露錯誤

很多開發人員都碰到過java.lang.OutOfMemoryError的錯誤。這種錯誤又分兩種:java.lang.OutOfMemoryError: Java heap spacejava.lang.OutOfMemoryError: PermGen space。引起這種錯誤的原因可能是程序問題,也可能是是JVM參數配置問題引起的。若是參數問題,前者可以同過配置-Xms-Xmx參數來設置,而後者可以通過配置 -XX:PermSize-XX:MaxPermSize來設置。



內存申請過程如下:

  1. JVM 會試圖爲相關Java對象在Eden中初始化一塊內存區域

  2. 當Eden空間足夠時,內存申請結束。否則到下一步

  3. JVM 試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收),釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區

  4. Survivor區被用來作爲Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區

  5. 當OLD區空間不夠時,JVM 會在OLD區進行完全的垃圾收集(0級)

  6. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden複製過來的部分對象,導致JVM無法在Eden區爲新對象創建內存區域,則出現”out of memory錯誤”


    垃圾收集的目的在於清除不再使用的對象。gc(Garbage Collector)通過確定對象是否被活動對象引用來確定是否收集該對象。gc首先要判斷該對象是否是時候可以收集。兩種常用的方法是引用計數和對象引用遍歷。

1.引用計數

    引用計數存儲對特定對象的所有引用數,也就是說,當應用程序創建引用以及引用超出範圍時,jvm必須適當增減引用數。當某對象的引用數爲0時,便可以進行垃圾收集。

2.對象引用遍歷

    早期的jvm使用引用計數,現在大多數jvm採用對象引用遍歷。對象引用遍歷從一組對象開始,沿着整個對象圖上的每條鏈接,遞歸確定可到達 (reachable)的對象。如果某對象不能從這些根對象的一個(至少一個)到達,則將它作爲垃圾收集。在對象遍歷階段,gc必須記住哪些對象可以到 達,以便刪除不可到達的對象,這稱爲標記(marking)對象。

    下一步,gc要刪除不可到達的對象。刪除時,有些gc只是簡單的掃描堆棧,刪除未標記的未標記的對象,並釋放它們的內存以生成新的對象,這叫做清除 (sweeping)。這種方法的問題在於內存會分成好多小段,而它們不足以用於新的對象,但是組合起來卻很大。因此,許多gc可以重新組織內存中的對 象,並進行壓縮(compact),形成可利用的空間。

    爲此,gc需要停止其他的活動活動。這種方法意味着所有與應用程序相關的工作停止,只有gc運行。結果,在響應期間增減了許多混雜請求。另外,更復雜的gc不斷增加或同時運行以減少或者清除應用程序的中斷。有的gc使用單線程完成這項工作,有的則採用多線程以增加效率。


幾種垃圾回收機制

1.標記-清除收集器

    這種收集器首先遍歷對象圖並標記可到達的對象,然後掃描堆棧(應該是掃描堆吧,因爲java對象在堆中。堆棧是棧的一種吧?)以尋找未標記對象並釋放它們的內存。這種收集器一般使用單線程工作並停止其他操作。

2.標記-壓縮收集器

    有時也叫標記-清除-壓縮收集器,與標記-清除收集器有相同的標記階段。在第二階段,則把標記對象複製到堆棧的新域中以便壓縮堆棧。這種收集器也停止其他操作。

3.複製收集器

    這種收集器將堆棧分爲兩個域,常稱爲半空間。每次僅使用一半的空間,jvm生成的新對象則放在另一半空間中。gc運行時,它把可到達對象複製到另一半空間,從而壓縮了堆棧。這種方法適用於短生存期的對象,持續複製長生存期的對象則導致效率降低。

4.增量收集器

    增量收集器把堆棧分爲多個域,每次僅從一個域收集垃圾。這會造成較小的應用程序中斷。

5.分代收集器

    這種收集器把堆棧分爲兩個或多個域,用以存放不同壽命的對象。jvm生成的新對象一般放在其中的某個域中。過一段時間,繼續存在的對象將獲得使用期並轉入更長壽命的域中。分代收集器對不同的域使用不同的算法以優化性能。

6.併發收集器

    併發收集器與應用程序同時運行。這些收集器在某點上(比如壓縮時)一般都不得不停止其他操作以完成特定的任務,但是因爲其他應用程序可進行其他的後臺操作,所以中斷其他處理的實際時間大大降低。

7.並行收集器

    並行收集器使用某種傳統的算法並使用多線程並行的執行它們的工作。在多cpu機器上使用多線程技術可以顯著的提高java應用程序的可擴展性。


參考:http://blog.csdn.net/autofei/article/details/7456213

      http://developer.51cto.com/art/200607/29278.htm

      http://blog.csdn.net/winniepu/article/details/4829087


 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章