深入Java垃圾收集

1. 判斷對象是否已死的方法

  1. 可達性分析

    可作爲GC Roots的對象包括下面幾種:

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

2. 垃圾收集算法:

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

    分標記和清除兩個階段,缺點:一是兩個階段效率低,二是產生內存碎片。

  2. 複製(Copying)算法:

    把內存平均分爲兩塊,每次只使用其中一塊,當這一塊的內存使用完了就將還存活的對象複製到另外一塊上面,然後在把使用過的內存空間一次清理掉。
    特點:實現簡單、運行高效。缺點是:每次只使用一半內存,浪費內存,而且如果存活對象比較多則需要複製很多對象,效率會變低。

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

    標記過程與標記-清除(Mark Sweep)算法一樣,然後讓存活的對象向一端移動,然後直接清理掉邊界以外的內存。

  4. 分代收集算法:

    大多數商業虛擬機都採用這種算法,一般把Java堆分爲新生代和老年代,根據各個年代的特點採取適當的收集算法,一般新生代採用複製算法,老年代採用標記-清理或標記-整理。

3. 垃圾收集器:

  1. Serial收集器

    新生代收集器,採用複製算法。
    特點:簡單高效,Client模式下默認的垃圾收集器。
    缺點:單線程進行垃圾收集,收集期間發生STW(Stop The World)暫停所有用戶線程。

  2. ParNew收集器

    Serial收集器的多線程版本。使用複製算法,新生代收集器,除了Serial之外唯一一個可以搭配CMS的收集器。

    隨着可以使用的CPU數量的增加,它對於GC時系統資源的有效利用還是很有好處的。它默認開啓的收集線程數與CPU的數量相同,在CPU非常多的環境下,可以使使用-XX:ParallelGCThreads參數來限制來限制垃圾收集的線程數。

  3. Parallel Scavenge收集器

    是一個新生代收集器,使用複製算法,並行的多線程收集器。關注吞吐量,目的爲達到一個可控的吞吐量,可通過參數-XX:MaxGCPauseMillis控制最大停頓時間,單位毫秒,參數-XX:GCTimeRatio控制垃圾收集時間佔總時間佔比,值爲大於0小於100的整數。參數-XX:UseAdaptiveSizePolicy開啓後可以自動調節新生代大小、Eden與Survivor區的比例以及晉升老年代對象大小。

  4. Serial Old收集器

    Serial收集器的老年代版本,單線程收集器,標記-整理算法進行收集,主要用於Client模式下。

  5. Parallel Old收集器

    Parallel Scavenge收集器的老年代版本。多線程,使用標記-整理算法。

  6. CMS收集器

    年老代收集器,一種以獲取最短回收停頓時間爲目標的收集器。基於“標記-清除”算法。收集過程包含如下四個步驟:
    1) 初始標記
    2) 併發標記
    3) 重新標記
    4) 併發清除
    其中1)、3)需要STW,但耗時很少,另外兩個耗時的過程收集線程可以和用戶線程一起工作。所以整體來說CMS收集器的內存回收過程是與用戶線程一起併發執行的。

    特點:併發收集(整體來說收集線程和用戶線程一起工作)、低停頓。
    缺點:

    1. 和用戶線程一起併發工作時會佔用一些CPU資源.
    2. 無法處理浮動垃圾,浮動垃圾是指在和用戶線程一起工作時用戶線程產生的垃圾無法回收,考慮到會和用戶線程一起工作所以會預留一部分(可配百分比)內存給用戶線程,在收集期間如果這部分預留的內存不滿足用戶線程就會導致“Concurrent Mode Failure”,從而使JVM啓動後備預案:臨時啓用Serial Old收集器來重新進行老年代的垃圾收集。
    3. 因爲基於“標記-清除”算法所以會產生內存碎片。

4 GC 日誌

  1. -XX:+UseSerialGC

    GC收集器:
    新生代:Serial
    老年代:SerialOld

新生代回收日誌:
[GC (Allocation Failure): [DefNew: 1664K->0K(1856K), 0.0001900 secs] 2105K->441K(5952K), 0.0002271 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

老年代回收日誌:
[Full GC (Allocation Failure) [Tenured: 4095K->4095K(4096K), 0.0096600 secs] 5951K->5544K(5952K), [Metaspace: 3373K->3373K(1056768K)], 0.0096927 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 

Heap
 def new generation   total 1856K, used 899K [0x00000007bfa00000, 0x00000007bfc00000, 0x00000007bfc00000)
  eden space 1664K,  54% used [0x00000007bfa00000, 0x00000007bfae0bf8, 0x00000007bfba0000)
  from space 192K,   0% used [0x00000007bfba0000, 0x00000007bfba0048, 0x00000007bfbd0000)
  to   space 192K,   0% used [0x00000007bfbd0000, 0x00000007bfbd0000, 0x00000007bfc00000)
 tenured generation   total 4096K, used 441K [0x00000007bfc00000, 0x00000007c0000000, 0x00000007c0000000)
   the space 4096K,  10% used [0x00000007bfc00000, 0x00000007bfc6e740, 0x00000007bfc6e800, 0x00000007c0000000)
 Metaspace       used 3378K, capacity 4564K, committed 4864K, reserved 1056768K
  class space    used 370K, capacity 388K, committed 512K, reserved 1048576K
  1. -XX:+UseParallelGC

    GC收集器:
    新生代:Parallel Scavenge
    老年代:SerialOld

新生代回收日誌:
[GC (Allocation Failure) [PSYoungGen: 1056K->64K(1536K)] 1553K->561K(5632K), 0.0005464 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

老年代回收日誌:
[Full GC (Ergonomics) [PSYoungGen: 1024K->981K(1536K)] [ParOldGen: 3870K->3870K(4096K)] 4894K->4852K(5632K), [Metaspace: 3371K->3371K(1056768K)], 0.0131815 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 

Heap
 PSYoungGen      total 1536K, used 309K [0x00000007bfe00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 1024K, 24% used [0x00000007bfe00000,0x00000007bfe3d7d0,0x00000007bff00000)
  from space 512K, 12% used [0x00000007bff00000,0x00000007bff10000,0x00000007bff80000)
  to   space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
 ParOldGen       total 4096K, used 497K [0x00000007bfa00000, 0x00000007bfe00000, 0x00000007bfe00000)
  object space 4096K, 12% used [0x00000007bfa00000,0x00000007bfa7c530,0x00000007bfe00000)
 Metaspace       used 3378K, capacity 4564K, committed 4864K, reserved 1056768K
  class space    used 370K, capacity 388K, committed 512K, reserved 1048576K
  1. -XX:+UseConcMarkSweepGC

    GC收集器:
    新生代:ParNew
    老年代:CMS

新生代回收日誌:
[GC (Allocation Failure) 11.273: [ParNew: 1090K->2K(1216K), 0.0014268 secs] 1546K->458K(6016K), 0.0015251 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

老年代回收日誌:
[Full GC (Allocation Failure) [CMS: 4800K->4800K(4800K), 0.0179098 secs] 6015K->6015K(6016K), [Metaspace: 3372K->3372K(1056768K)], 0.0179558 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 

Heap
 par new generation   total 1216K, used 35K [0x00000007bfa00000, 0x00000007bfb50000, 0x00000007bfb50000)
  eden space 1088K,   3% used [0x00000007bfa00000, 0x00000007bfa08588, 0x00000007bfb10000)
  from space 128K,   1% used [0x00000007bfb30000, 0x00000007bfb30810, 0x00000007bfb50000)
  to   space 128K,   0% used [0x00000007bfb10000, 0x00000007bfb10000, 0x00000007bfb30000)
 concurrent mark-sweep generation total 4800K, used 456K [0x00000007bfb50000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 3376K, capacity 4564K, committed 4864K, reserved 1056768K
  class space    used 370K, capacity 388K, committed 512K, reserved 1048576K
  1. -XX:+UseParNewGC(java8下提示過時)

    GC收集器:
    新生代:ParNew
    老年代:SerialOld

8.506: [GC (Allocation Failure) 8.506: [ParNew: 1666K->2K(1856K), 0.0002354 secs] 2214K->550K(5952K), 0.0002726 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 1856K, used 1400K [0x00000007bfa00000, 0x00000007bfc00000, 0x00000007bfc00000)
  eden space 1664K,  84% used [0x00000007bfa00000, 0x00000007bfb5db38, 0x00000007bfba0000)
  from space 192K,   1% used [0x00000007bfba0000, 0x00000007bfba0810, 0x00000007bfbd0000)
  to   space 192K,   0% used [0x00000007bfbd0000, 0x00000007bfbd0000, 0x00000007bfc00000)
 tenured generation   total 4096K, used 548K [0x00000007bfc00000, 0x00000007c0000000, 0x00000007c0000000)
   the space 4096K,  13% used [0x00000007bfc00000, 0x00000007bfc89268, 0x00000007bfc89400, 0x00000007c0000000)
 Metaspace       used 3378K, capacity 4564K, committed 4864K, reserved 1056768K
  class space    used 370K, capacity 388K, committed 512K, reserved 1048576K
  1. 解釋

    GC (Allocation Failure):表示新生代發生的垃圾回收日誌。
    Full GC (Allocation Failure):表示老年代和元空間發生的垃圾回收日誌。

    [gc收集器名稱:gc前該內存空間使用量-> gc後該內存空間使用量(該內存空間總大小)]

5. 內存分配與回收策略

  1. 對象優先在Eden分配

    大多數情況下,對象在新生代Eden中分配。當Eden沒有足夠空間進行分配時,虛擬機將發起一次Minor GC。默認 Eden: From Survivor: To Survivor = 8:1:1

  2. 大對象直接進入老年代

    大對象是指需要大量連續空間的Java對象,最典型的就是很長的字符串以及數組。參數-XX:PretenureSizeThreshold可以指定這個閾值。

  3. 長期存活的對象將進入老年代

    虛擬機給每個對象定義了一個對象年齡(Age)計數器。如果對象在Eden出生並經歷過第一次Minor GC後仍然存活,並且能被Survivor容納的話,將被移動到Survivor空間中,並且對象年齡設置爲1。對象在Survivor區中每“熬過”一次Minor GC,年齡就增加1,當它的年齡達到一定程度(默認15),將會被晉升到老年代。對象晉升老年代的年齡閾值,可以通過參數-XX:MaxTenuringThreshold設置。

  4. 動態對象年齡判定

    爲了能更好地適應不同程序的內存狀況,虛擬機並不是永遠地要求對象的年齡必須達到MaxTenuringThreshold才能晉升到老年代,如果在Survivor空間中相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,無需等到MaxTenuringThreshold中要求的年齡。

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