JVM優化之垃圾收集器以及內存分配

 

在jvm中,實現了多種垃圾收集器,包括:串行垃圾收集器、並行垃圾收集器、CMS(併發)垃圾收集器、G1垃圾收集器,接下來,咱們一個個的瞭解學習。java


串行垃圾收集器web

串行垃圾收集器,是指使用單線程進行垃圾回收,垃圾回收時,只有一個線程在工做,算法

而且java應用中的全部線程都要暫停,等待垃圾回收的完成。這種現象稱之爲STW(Stop-The-World)。多線程

對於交互性較強的應用而言,這種垃圾收集器是不可以接受的。併發

通常在Javaweb應用中是不會採用該收集器的。oracle

編寫測試代碼dom

package com.wish;


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

public class TestGC {
    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);
        }

    }
}

  

設置垃圾回收爲串行收集器jvm

在程序運行參數中添加2個參數,以下:性能

  • -XX:+UseSerialGC
    • 指定年輕代和老年代都使用串行垃圾收集器
  • -XX:+PrintGCDetails
    • 打印垃圾回收的詳細信息
# 爲了測試GC,將堆的初始和最大內存都設置爲16M
-XX:+UseSerialGC -XX:+PrintGCDetails -Xms16m -Xmx16m

  

 

 

 啓動程序,能夠看到下面信息:學習

 

 

 

GC日誌信息解讀:
年輕代的內存GC先後的大小:

  • DefNew
    • 表示使用的是串行垃圾收集器。
  • 4416K->4416K(4928K)
    • 表示,年輕代GC前,佔有4416K內存,GC後,佔有4416K內存,總大小4928K
  • 0.0046102 secs
    • 表示,GC所用的時間,單位爲毫秒。
  • 14807k->12823k(15872K)
    • 表示,GC前,堆內存佔有4416K,GC後,佔有1973K,總大小爲15872K
  • Full GC
    • 表示,內存空間所有進行GC

 

並行垃圾收集器

並行垃圾收集器在串行垃圾收集器的基礎之上作了改進,將單線程改成了多線程進行垃圾回收,這樣能夠縮短垃圾回收的時間。

(這裏是指,並行能力較強的機器)

固然了,並行垃圾收集器在收集的過程當中也會暫停應用程序,這個和串行垃圾回收器是同樣的,只是並行執行,速度更快些,暫停的時間更短一些。

ParNew垃圾收集器

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

測試:

 

 

 

#參數
-XX:+UseParNewGC -XX:+PrintGCDetails -Xms16m -Xmx16m

  

 

 

 

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

 

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模式,垃圾回收器將自動調全年輕代、老年代等參數,達到吞吐量、堆大小、停頓時間之間的平衡。
    • 通常用於,手動調整參數比較困難的場景,讓收集器自動進行調整。

測試

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

  

啓動

 

 

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

 

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),與用戶線程同時運行;

測試

#設置啓動參數
‐XX:+UseConcMarkSweepGC ‐XX:+PrintGCDetails ‐Xms16m ‐Xmx16m

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 由以上日誌信息,能夠看出CMS執行的過程。

G1垃圾收集器

G1垃圾收集器是在jdk1.7中正式使用的全新的垃圾收集器,oracle官方計劃在jdk9中將G1變成默認的垃圾收集器,以替代CMS。

G1的設計原則就是簡化JVM性能調優,開發人員只須要簡單的三步便可完成調優:

1. 第一步,開啓G1垃圾收集器

2. 第二步,設置堆的最大內存

3. 第三步,設置最大的停頓時間

G1中提供了三種模式垃圾回收模式,Young GC、Mixed GC 和 Full GC,在不一樣的條件下被觸發。

原理

G1垃圾收集器相對比其餘收集器而言,最大的區別在於它取消了年輕代、老年代的物理劃分,

取而代之的是將堆劃分爲若干個區域(Region),這些區域中包含了有邏輯上的年輕代、老年代區域。

這樣作的好處就是,咱們不再用單獨的空間對每一個代進行設置了,不用擔憂每一個代內存是否足夠。

 

 

 

 

 

 在G1劃分的區域中,年輕代的垃圾收集依然採用暫停全部應用線程的方式,將存活對象拷貝到老年代或者Survivor空間,

G1收集器經過將對象從一個區域複製到另一個區域,完成了清理工做。

這就意味着,在正常的處理過程當中,G1完成了堆的壓縮(至少是部分堆的壓縮),這樣也就不會有cms內存碎片問題的存在了。

在G1中,有一種特殊的區域,叫Humongous區域。

  • 若是一個對象佔用的空間超過了分區容量50%以上,G1收集器就認爲這是一個巨型對象。
  • 這些巨型對象,默認直接會被分配在老年代,可是若是它是一個短時間存在的巨型對象,就會對垃圾收集器形成負面影響。
  • 爲了解決這個問題,G1劃分了一個Humongous區,它用來專門存放巨型對象。若是一個H區裝不下一個巨型對象,那麼G1會尋找連續的H分區來存儲。爲了能找到連續的H區,有時候不得不啓動Full GC。

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。

 

Mixed GC

當愈來愈多的對象晉升到老年代old region時,爲了不堆內存被耗盡,虛擬機會觸發一個混合的垃圾收集器,

即Mixed GC,該算法並非一個Old GC,除了回收整個YoungRegion,還會回收一部分的Old Region,

這裏須要注意:是一部分老年代,而不是所有老年代,能夠選擇哪些old region進行收集,從而能夠對垃圾回收的耗時時間進行控制。

也要注意的是Mixed GC 並非 Full GC。

MixedGC何時觸發?

由參數 -XX:InitiatingHeapOccupancyPercent=n 決定。默認:45%,該參數的意思是:當老年代大小佔整個堆大小百分比達到該閥值時觸發。

它的GC步驟分2步:

1.全局併發標記(global concurrent marking)

2. 拷貝存活對象(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中,從而實現垃圾的回收清理。

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%。

測試

#參數
‐XX:+UseG1GC ‐XX:MaxGCPauseMillis=100 ‐XX:+PrintGCDetails ‐Xmx256m

  

 

 

 

 

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

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

相關文章
相關標籤/搜索