垃圾收集器以及內存分配

1、分類

  在jvm中,實現了多種垃圾收集器,包括:串行垃圾收集器、並行垃圾收集器、CMS(併發)垃圾收集器、G1垃圾收集器

2、串行垃圾收集器

  串行垃圾收集器,是指使用單線程進行垃圾回收,垃圾回收時,只有一個線程在工做, 而且java應用中的全部線程都要暫停,等待垃圾回收的完成。這種現象稱之爲 STW(Stop-The-World)。
  對於交互性較強的應用而言,這種垃圾收集器是不可以接受的。
  通常在Javaweb應用中是不會採用該收集器的。 

一、編寫測試代碼

package com.zn; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Random; public class GCTest { public static void main(String[] args) throws Exception { List<Object> list = new ArrayList<Object>(); while (true) { int sleep = new Random().nextInt(100); if (System.currentTimeMillis() % 2 == 0) { list.clear(); } else { for (int i = 0; i < 10000; i++) { Properties properties = new Properties(); properties.put("key_" + i, "value_" + System.currentTimeMillis() + i); list.add(properties); } } // System.out.println("list大小爲:" + list.size()); 
 Thread.sleep(sleep); } } }

二、設置垃圾回收爲串行收集器

  在程序運行參數中添加2個參數,以下:
    -XX:+UseSerialGC:指定年輕代和老年代都使用串行垃圾收集器
    -XX:+PrintGCDetails:打印垃圾回收的詳細信息
-XX:+UseSerialGC -XX:+PrintGCDetails -Xms16m -Xmx16m

   

三、啓動程序信息打印

  

GC日誌信息解讀:
  年輕代的內存GC先後的大小:
  DefNew 表示使用的是串行垃圾收集器
    4416K->512K(4928K): 表示,年輕代GC前,佔有4416K內存,GC後,佔有512K內存,總大小4928K 
  0.0043929 secs 表示,GC所用的時間,單位爲毫秒。
  11130K->9711K(15872K) 表示,GC前,堆內存佔有11130K,GC後,佔有9711K,總大小爲15872K
  Full GC 表示,內存空間所有進行GC

2、並行垃圾收集器

  並行垃圾收集器在串行垃圾收集器的基礎之上作了改進,將單線程改成了多線程進行垃 圾回收,這樣能夠縮短垃圾回收的時間。(這裏是指,並行能力較強的機器) 固然了,並行垃圾收集器在收集的過程當中也會暫停應用程序,這個和串行垃圾回收器是 同樣的,只是並行執行,速度更快些,暫停的時間更短一些。

2.1  ParNew垃圾收集器

  ParNew垃圾收集器是工做在年輕代上的,只是將串行的垃圾收集器改成了並行。
   經過-XX:+UseParNewGC參數設置年輕代使用ParNew回收器,老年代使用的依然是串行 收集器。

  一、編寫測試代碼

package com.zn; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Random; public class GCTest { public static void main(String[] args) throws Exception { List<Object> list = new ArrayList<Object>(); while (true) { int sleep = new Random().nextInt(100); if (System.currentTimeMillis() % 2 == 0) { list.clear(); } else { for (int i = 0; i < 10000; i++) { Properties properties = new Properties(); properties.put("key_" + i, "value_" + System.currentTimeMillis() + i); list.add(properties); } // System.out.println("list大小爲:" + list.size());
 Thread.sleep(sleep); } } }

  二、設置垃圾回收爲ParNew收集器

-XX:+UseParNewGC -XX:+PrintGCDetails -Xms16m -Xmx16m

  

  三、啓動程序信息打印

  

  由以上信息能夠看出, ParNew: 使用的是ParNew收集器。其餘信息和串行收集器一致。html

2.2  ParallelGC垃圾收集器 

  ParallelGC收集器工做機制和ParNewGC收集器同樣,只是在此基礎之上,新增了兩個和 系統吞吐量相關的參數,使得其使用起來更加的靈活和高效。
  相關參數以下:
    -XX:+UseParallelGC 年輕代使用ParallelGC垃圾回收器,老年代使用串行回收器。
    -XX:+UseParallelOldGC 年輕代使用ParallelGC垃圾回收器,老年代使用ParallelOldGC垃圾回收器。
    -XX:MaxGCPauseMillis 設置最大的垃圾收集時的停頓時間,單位爲毫秒
      須要注意的是,ParallelGC爲了達到設置的停頓時間,可能會調整堆大小或其餘 的參數,若是堆的大小設置的較小,就會致使GC工做變得很頻繁,反而可能會 影響到性能。
       該參數使用需謹慎。
    -XX:GCTimeRatio 設置垃圾回收時間佔程序運行時間的百分比,公式爲1/(1+n)。
       它的值爲0~100之間的數字,默認值爲99,也就是垃圾回收時間不能超過1%
    -XX:UseAdaptiveSizePolicy 自適應GC模式,垃圾回收器將自動調全年輕代、老年代等參數,達到吞吐量、 堆大小、停頓時間之間的平衡。
      通常用於,手動調整參數比較困難的場景,讓收集器自動進行調整。

  一、編寫測試代碼

package com.zn; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Random; public class GCTest { public static void main(String[] args) throws Exception { List<Object> list = new ArrayList<Object>(); while (true) { int sleep = new Random().nextInt(100); if (System.currentTimeMillis() % 2 == 0) { list.clear(); } else { for (int i = 0; i < 10000; i++) { Properties properties = new Properties(); properties.put("key_" + i, "value_" + System.currentTimeMillis() + i); list.add(properties); } // System.out.println("list大小爲:" + list.size());  Thread.sleep(sleep); } } }

  二、設置垃圾回收爲ParallelGC收集器

-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=100
-XX:+PrintGCDetails -Xms16m -Xmx16m

  

  三、啓動程序信息打印

  

   由以上信息能夠看出,年輕代和老年代都使用了ParallelGC垃圾回收器。

3、CMS垃圾收集器

  CMS全稱 Concurrent Mark Sweep,是一款併發的、使用標記-清除算法的垃圾回收器, 該回收器是針對老年代垃圾回收的,經過參數-XX:+UseConcMarkSweepGC進行設置。
  CMS垃圾回收器的執行過程以下: 

  

  • 初始化標記(CMS-initial-mark) ,標記root,會致使stw;
  • 併發標記(CMS-concurrent-mark),與用戶線程同時運行;
  • 預清理(CMS-concurrent-preclean),與用戶線程同時運行;
  • 從新標記(CMS-remark) ,會致使stw;
  • 併發清除(CMS-concurrent-sweep),與用戶線程同時運行;
  • 調整堆大小,設置CMS在清理以後進行內存壓縮,目的是清理內存中的碎片;
  • 併發重置狀態等待下次CMS的觸發(CMS-concurrent-reset),與用戶線程同時運行; 

一、編寫測試代碼

package com.zn; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Random; public class GCTest { public static void main(String[] args) throws Exception { List<Object> list = new ArrayList<Object>(); while (true) { int sleep = new Random().nextInt(100); if (System.currentTimeMillis() % 2 == 0) { list.clear(); } else { for (int i = 0; i < 10000; i++) { Properties properties = new Properties(); properties.put("key_" + i, "value_" + System.currentTimeMillis() + i); list.add(properties); } } // System.out.println("list大小爲:" + list.size());  Thread.sleep(sleep); } } }

二、設置垃圾回收爲CMS收集器

-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -Xms16m -Xmx16m

  

三、啓動程序信息打印

  CMS執行的過程:

  

4、G1垃圾收集器(重點)

  G1垃圾收集器是在jdk1.7中正式使用的全新的垃圾收集器,oracle官方計劃在jdk9中將 G1變成默認的垃圾收集器,以替代CMS。 
  G1的設計原則就是簡化JVM性能調優,開發人員只須要簡單的三步便可完成調優:
    1. 第一步,開啓G1垃圾收集器
    2. 第二步,設置堆的最大內存
    3. 第三步,設置最大的停頓時間
  G1中提供了三種模式垃圾回收模式,Young GC、Mixed GC 和 Full GC,在不一樣的條件 下被觸發。

4.1  原理

  G1垃圾收集器相對比其餘收集器而言,最大的區別在於它取消了年輕代、老年代的物理 劃分,取而代之的是將堆劃分爲若干個區域(Region),這些區域中包含了有邏輯上的 年輕代、老年代區域。
  這樣作的好處就是,咱們不再用單獨的空間對每一個代進行設置了,不用擔憂每一個代內 存是否足夠。

  

  在G1劃分的區域中,年輕代的垃圾收集依然採用暫停全部應用線程的方式,將存活對象 拷貝到老年代或者Survivor空間,G1收集器經過將對象從一個區域複製到另一個區 域,完成了清理工做。
  這就意味着,在正常的處理過程當中,G1完成了堆的壓縮(至少是部分堆的壓縮),這樣 也就不會有cms內存碎片問題的存在了。
  在G1中,有一種特殊的區域,叫Humongous區域
    若是一個對象佔用的空間超過了分區容量50%以上,G1收集器就認爲這是一個巨型 對象。
    這些巨型對象,默認直接會被分配在老年代,可是若是它是一個短時間存在的巨型對 象,就會對垃圾收集器形成負面影響。
    爲了解決這個問題,G1劃分了一個Humongous區,它用來專門存放巨型對象。若是 一個H區裝不下一個巨型對象,那麼G1會尋找連續的H分區來存儲。爲了能找到連續 的H區,有時候不得不啓動Full GC。

4.2  Young GC

  Young GC主要是對Eden區進行GC,它在Eden空間耗盡時會被觸發。
    Eden空間的數據移動到Survivor空間中,若是Survivor空間不夠,Eden空間的部分 數據會直接晉升到年老代空間。
    Survivor區的數據移動到新的Survivor區中,也有部分數據晉升到老年代空間中。
    最終Eden空間的數據爲空,GC中止工做,應用線程繼續執行。
  
  

   Remembered Set(已記憶集合)

  在GC年輕代的對象時,咱們如何找到年輕代中對象的根對象呢?
    根對象多是在年輕代中,也能夠在老年代中,那麼老年代中的全部對象都是根麼?
  若是全量掃描老年代,那麼這樣掃描下來會耗費大量的時間。
    因而,G1引進了RSet的概念。它的全稱是Remembered Set,其做用是跟蹤指向某個堆 內的對象引用。

     

  每一個Region初始化時,會初始化一個RSet,該集合用來記錄並跟蹤其它Region指向該 Region中對象的引用,每一個Region默認按照512Kb劃分紅多個Card,因此RSet須要記錄 的東西應該是 xx Region的 xx Card。 

4.3  Mixed GC

  當愈來愈多的對象晉升到老年代old region時,爲了不堆內存被耗盡,虛擬機會觸發一 個混合的垃圾收集器,即Mixed GC,該算法並非一個Old GC,除了回收整個Young
  Region,還會回收一部分的Old Region,這裏須要注意:是一部分老年代,而不是所有
  老年代,能夠選擇哪些old region進行收集,從而能夠對垃圾回收的耗時時間進行控制。
  也要注意的是Mixed GC 並非 Full GC。
  MixedGC何時觸發?
    由參數 -XX:InitiatingHeapOccupancyPercent=n 決定。默 認:45%,該參數的意思是:當老年代大小佔整個堆大小百分比達到該閥值時觸發。
  它的GC步驟分2步:
    ①全局併發標記(global concurrent marking)
    ② 拷貝存活對象(evacuation)

  一、全局併發標記

  全局併發標記,執行過程分爲五個步驟:
    初始標記(initial mark,STW)
      標記從根節點直接可達的對象,這個階段會執行一次年輕代GC,會產生全局停 頓。
    根區域掃描(root region scan)
      G1 GC 在初始標記的存活區掃描對老年代的引用,並標記被引用的對象。
      該階段與應用程序(非 STW)同時運行,而且只有完成該階段後,才能開始下
      一次 STW 年輕代垃圾回收。
    併發標記(Concurrent Marking)
      G1 GC 在整個堆中查找可訪問的(存活的)對象。該階段與應用程序同時運行, 能夠被 STW 年輕代垃圾回收中斷。
    從新標記(Remark,STW)
      該階段是 STW 回收,由於程序在運行,針對上一次的標記進行修正。
    清除垃圾(Cleanup,STW)
      清點和重置標記狀態,該階段會STW,這個階段並不會實際上去作垃圾的收集, 等待evacuation階段來回收。 

  二、拷貝存活對象

    Evacuation階段是全暫停的。該階段把一部分Region裏的活對象拷貝到另外一部分Region 中,從而實現垃圾的回收清理。 

4.4  G1收集器相關參數

  -XX:+UseG1GC 使用 G1 垃圾收集器
  -XX:MaxGCPauseMillis 設置指望達到的最大GC停頓時間指標(JVM會盡力實現,但不保證達到),默認 值是 200 毫秒。
  -XX:G1HeapRegionSize=n 設置的 G1 區域的大小。值是 2 的冪,範圍是 1 MB 到 32 MB 之間。目標是根 據最小的 Java 堆大小劃分出約 2048 個區域。 默認是堆內存的1/2000。
  -XX:ParallelGCThreads=n 設置 STW 工做線程數的值。將 n 的值設置爲邏輯處理器的數量。n 的值與邏輯, 處理器的數量相同,最多爲 8。
  -XX:ConcGCThreads=n 設置並行標記的線程數。將 n 設置爲並行垃圾回收線程數 (ParallelGCThreads) 的 1/4 左右。
  -XX:InitiatingHeapOccupancyPercent=n 設置觸發標記週期的 Java 堆佔用率閾值。默認佔用率是整個 Java 堆的 45%。  

4.5  測試

  一、編寫測試代碼

package com.zn;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Random;

public class GCTest {
    public static void main(String[] args) throws Exception {
        List<Object> list = new ArrayList<Object>();
        while (true) {
            int sleep = new Random().nextInt(100);
            if (System.currentTimeMillis() % 2 == 0) {
                list.clear();
            } else {
                for (int i = 0; i < 10000; i++) {
                    Properties properties = new Properties();
                    properties.put("key_" + i, "value_" + System.currentTimeMillis() + i);
                    list.add(properties);
                }
            
            // System.out.println("list大小爲:" + list.size());
            Thread.sleep(sleep);
        }
    }
}

  二、設置垃圾回收爲G1 垃圾收集器

-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xmx256m

  

  三、啓動程序信息打印

  

4.6  對於G1垃圾收集器優化建議

  年輕代大小
    避免使用 -Xmn 選項或 -XX:NewRatio 等其餘相關選項顯式設置年輕代大小。
    固定年輕代的大小會覆蓋暫停時間目標。
  暫停時間目標不要太過嚴苛
    G1 GC 的吞吐量目標是 90% 的應用程序時間和 10%的垃圾回收時間。
    評估 G1 GC 的吞吐量時,暫停時間目標不要太嚴苛。目標太過嚴苛表示您願意 承受更多的垃圾回收開銷,而這會直接影響到吞吐量

 

原文出處:https://www.cnblogs.com/Zzzzn/p/12411463.htmljava

相關文章
相關標籤/搜索