JVM 面試

JVM 垃圾回收的時候如何肯定垃圾?知道什麼是 GC Roots ?

  • 什麼是垃圾
    • 簡單來講就是內存中已經不在被使用到的空間就是垃圾
  • 要進行垃圾回收,如何判斷一個對象是否能夠被回收?
    • 引用計數法
    • 枚舉根節點作可達性分析

爲了解決引用計數法的循環引用問題,Java 使用了可達性算法。html

img

跟蹤收集器採用的爲集中式的管理方式,全局記錄對象之間的引用狀態,執行時從一些列GC Roots的對象作爲起點,從這些節點向下開始進行搜索全部的引用鏈,當一個對象到GC Roots 沒有任何引用鏈時,則證實此對象是不可用的。java

圖中,對象Object六、Object七、Object8雖然互相引用,但他們的GC Roots是不可到達的,因此它們將會被斷定爲是可回收的對象。ios

哪些對象能夠做爲 GC Roots 的對象:算法

  • 虛擬機棧(棧幀中的局部變量區,也叫局部變量表)中引用的對象
  • 方法區中的類靜態屬性引用的對象
  • 方法去常量引用的對象
  • 本地方法棧中 JNI (Native方法)引用的對象

你說你作過 JVM 調優和參數配置,請問若是盤點查看 JVM 系統默認值?

JVM 的參數類型

  • 標配參數
    • -version
    • -help
  • X 參數(瞭解)
    • -Xint:解釋執行
    • -Xcomp:第一次使用就編譯成本地代碼
    • -Xmixed:混合模式
  • XX 參數緩存

    • Boolean 類型:-XX:+ 或者 - 某個屬性值(+ 表示開啓,- 表示關閉)
      • -XX:+PrintGCDetails:打印 GC 收集細節
      • -XX:-PrintGCDetails:不打印 GC 收集細節
      • -XX:+UseSerialGC:使用了串行收集器
      • -XX:-UseSerialGC:不使用了串行收集器
    • KV 設置類型:-XX:key=value
      • -XX:MetaspaceSize=128m
      • -XX:MaxTenuringThreshold=15
    • jinfo 舉例,如何查看當前運行程序的配置服務器

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class HelloGC {
          public static void main(String[] args) {
              System.out.println("hello GC...");
              try {
                  Thread.sleep(Integer.MAX_VALUE);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }

      咱們可使用 jps -l 命令,查出進程 id網絡

      1
      2
      3
      4
      5
      6
      1923 org.jetbrains.jps.cmdline.Launcher
      1988 sun.tools.jps.Jps
      1173 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
      32077 com.intellij.idea.Main
      1933 com.cuzz.jvm.HelloGC
      32382 org.jetbrains.idea.maven.server.RemoteMavenServer

      在使用 jinfo -flag PrintGCDetails 1933 命令查看併發

      1
      -XX:-PrintGCDetails

      能夠看出默認是不打印 GC 收集細節jvm

      也但是使用jinfo -flags 1933 查看因此的參數maven

    • 兩個經典參數:-Xms 和 - Xmx(如 -Xms1024m)

      • -Xms 等價於 -XX:InitialHeapSize
      • -Xmx 等價於 -XX:MaxHeapSize

盤點家底查看 JVM 默認值

  • 查看初始默認值:-XX:+PrintFlagsInitial

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    cuzz@cuzz-pc:~/Project/demo$ java -XX:+PrintFlagsInitial
    [Global flags]
         intx ActiveProcessorCount                      = -1                                  {product}
        uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
        uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
        uintx AdaptiveSizePausePolicy                   = 0                                   {product}
        uintx AdaptiveSizePolicyCollectionCostMargin    = 50                                  {product}
        uintx AdaptiveSizePolicyInitializingSteps       = 20                                  {product}
        uintx AdaptiveSizePolicyOutputInterval          = 0                                   {product}
        uintx AdaptiveSizePolicyWeight                  = 10                                  {product}
       ...
  • 查看修改更新:-XX:+PrintFlagsFinal

    1
    2
    3
    4
    5
    6
    7
    bool UsePSAdaptiveSurvivorSizePolicy           = true                                {product}
    bool UseParNewGC                               = false                               {product}
    bool UseParallelGC                            := true                                {product}
    bool UseParallelOldGC                          = true                                {product}
    bool UsePerfData                               = true                                {product}
    bool UsePopCountInstruction                    = true                                {product}
    bool UseRDPCForConstantTableBase               = false                               {C2 product}

    = 與 := 的區別是,一個是默認,一個是人物改變或者 jvm 加載時改變的參數

  • 打印命令行參數(能夠看默認垃圾回收器):-XX:+PrintCommandLineFlags

    1
    2
    cuzz@cuzz-pc:~/Project/demo$ java -XX:+PrintCommandLineFlags
    -XX:InitialHeapSize=128789376 -XX:MaxHeapSize=2060630016 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC

你平時工做用過的 JVM 經常使用的基本配置參數有哪些?

  • -Xms
    • 初始大小內存,默認爲物理內存 1/64
    • 等價於 -XX:InitialHeapSize
  • -Xmx
    • 最大分配內存,默認爲物理內存的 1/4
    • 等價於 -XX:MaxHeapSize
  • -Xss
    • 設置單個線程棧的大小,通常默認爲 512-1024k
    • 等價於 -XX:ThreadStackSize
  • -Xmn
    • 設置年輕代的大小
    • 整個JVM內存大小=年輕代大小 + 年老代大小 + 持久代大小,持久代通常固定大小爲64m,因此增大年輕代後,將會減少年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
  • -XX:MetaspaceSize
    • 設置元空間大小(元空間的本質和永久代相似,都是對 JVM 規範中的方法區的實現,不過元空間於永久代之間最大區別在於,元空間並不在虛擬中,而是使用本地內存,所以默認狀況下,元空間的大小僅受本地內存限制)
    • 元空間默認比較小,咱們能夠調大一點
  • -XX:+PrintGCDetails

    • 輸出詳細 GC 收集日誌信息

      • 設置 JVM 參數爲: -Xms10m -Xmx10m -XX:+PrintGCDetails
      • 代碼

        1
        2
        3
        4
        5
        public class HelloGC {
            public static void main(String[] args) {
                byte[] bytes = new byte[20 * 1024 * 1024];
            }
        }
      • 打印結果

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        [GC (Allocation Failure) [PSYoungGen: 1231K->448K(2560K)] 1231K->456K(9728K), 0.0015616 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
        [GC (Allocation Failure) [PSYoungGen: 448K->384K(2560K)] 456K->392K(9728K), 0.0016999 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
        [Full GC (Allocation Failure) [PSYoungGen: 384K->0K(2560K)] [ParOldGen: 8K->358K(7168K)] 392K->358K(9728K), [Metaspace: 3028K->3028K(1056768K)], 0.0066696 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
        [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 358K->358K(9728K), 0.0005321 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
        [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 358K->340K(7168K)] 358K->340K(9728K), [Metaspace: 3028K->3028K(1056768K)], 0.0051543 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
        Heap
         PSYoungGen      total 2560K, used 81K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
          eden space 2048K, 3% used [0x00000000ffd00000,0x00000000ffd14668,0x00000000fff00000)
          from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
          to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
         ParOldGen       total 7168K, used 340K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
          object space 7168K, 4% used [0x00000000ff600000,0x00000000ff655188,0x00000000ffd00000)
         Metaspace       used 3060K, capacity 4496K, committed 4864K, reserved 1056768K
          class space    used 336K, capacity 388K, committed 512K, reserved 1048576K
        Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        	at com.cuzz.jvm.HelloGC.main(HelloGC.java:12)
    • GC
      img

    • FullGC
      img
  • -XX:SurvivorRatio
    • 設置新生代中 eden 和 S0/S1 空間比例
    • 默認 -XX:SurvivorRatio=8,Eden : S0 : S1 = 8 : 1 : 1
  • -XX:NewRatio
    • 配置年輕代和老年代在堆結構的佔比
    • 默認 -XX:NewRatio=2 新生代佔1,老年代佔2,年輕代佔整個堆的 1/3
  • -XX:MaxTenuringThreshold

    • 設置垃圾最大年齡

強引用、軟引用、弱引用和虛引用分別是什麼?

在Java語言中,除了基本數據類型外,其餘的都是指向各種對象的對象引用;Java中根據其生命週期的長短,將引用分爲4類。

  • 強引用

    • 特色:咱們日常典型編碼Object obj = new Object()中的obj就是強引用。經過關鍵字new建立的對象所關聯的引用就是強引用。 當JVM內存空間不足,JVM寧願拋出OutOfMemoryError運行時錯誤(OOM),使程序異常終止,也不會靠隨意回收具備強引用的「存活」對象來解決內存不足的問題。對於一個普通的對象,若是沒有其餘的引用關係,只要超過了引用的做用域或者顯式地將相應(強)引用賦值爲 null,就是能夠被垃圾收集的了,具體回收時機仍是要看垃圾收集策略。
  • 軟引用

    • 特色:軟引用經過SoftReference類實現。 軟引用的生命週期比強引用短一些。只有當 JVM 認爲內存不足時,纔會去試圖回收軟引用指向的對象:即JVM 會確保在拋出 OutOfMemoryError 以前,清理軟引用指向的對象。軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。後續,咱們能夠調用ReferenceQueue的poll()方法來檢查是否有它所關心的對象被回收。若是隊列爲空,將返回一個null,不然該方法返回隊列中前面的一個Reference對象。
    • 應用場景:軟引用一般用來實現內存敏感的緩存。若是還有空閒內存,就能夠暫時保留緩存,當內存不足時清理掉,這樣就保證了使用緩存的同時,不會耗盡內存。
    • 代碼驗證
      我設置 JVM 參數爲 -Xms10m -Xmx10m -XX:+PrintGCDetails

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      public class SoftReferenceDemo {
          public static void main(String[] args) {
              Object obj = new Object();
              SoftReference<Object> softReference = new SoftReference<>(obj);
              obj = null;
      
              try {
                  // 分配 20 M
                  byte[] bytes = new byte[20 * 1024 * 1024];
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  System.out.println("軟引用:" + softReference.get());
              }
      
          }
      }

      輸出

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      [GC (Allocation Failure) [PSYoungGen: 1234K->448K(2560K)] 1234K->456K(9728K), 0.0016748 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
      [GC (Allocation Failure) [PSYoungGen: 448K->384K(2560K)] 456K->392K(9728K), 0.0018398 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
      [Full GC (Allocation Failure) [PSYoungGen: 384K->0K(2560K)] [ParOldGen: 8K->358K(7168K)] 392K->358K(9728K), [Metaspace: 3030K->3030K(1056768K)], 0.0057246 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
      [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 358K->358K(9728K), 0.0006038 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
      [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 358K->340K(7168K)] 358K->340K(9728K), [Metaspace: 3030K->3030K(1056768K)], 0.0115080 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
      軟引用:null
      Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
      	at com.cuzz.jvm.SoftReferenceDemo.main(SoftReferenceDemo.java:21)
      Heap
       PSYoungGen      total 2560K, used 98K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
        eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd18978,0x00000000fff00000)
        from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
        to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
       ParOldGen       total 7168K, used 340K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
        object space 7168K, 4% used [0x00000000ff600000,0x00000000ff6552f8,0x00000000ffd00000)
       Metaspace       used 3067K, capacity 4496K, committed 4864K, reserved 1056768K
        class space    used 336K, capacity 388K, committed 512K, reserved 1048576K

      發現當內存不夠的時候就會被回收。

  • 弱引用

    • 特色:弱引用經過WeakReference類實現。 弱引用的生命週期比軟引用短。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快回收弱引用的對象。弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
    • 應用場景:弱應用一樣可用於內存敏感的緩存。
    • 代碼驗證

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class WeakReferenceDemo {
          public static void main(String[] args) {
              Object obj = new Object();
              WeakReference<Object> weakReference = new WeakReference<>(obj);
              System.out.println(obj);
              System.out.println(weakReference.get());
      
              obj = null;
              System.gc();
              System.out.println("GC以後....");
              
              System.out.println(obj);
              System.out.println(weakReference.get());
          }
      }

      輸出

      1
      2
      3
      4
      5
      java.lang.Object@1540e19d
      java.lang.Object@1540e19d
      GC以後....
      null
      null

      值得注意的是String name = "cuzz" 這種會放入永久代,以及 Integer age = 1 在 int 中 -128 到 127 會被緩存,因此是強引用,而後 GC 也不會被回收。

    • 引用隊列

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public class ReferenceQueueDemo {
          public static void main(String[] args) throws InterruptedException {
              Object obj = new Object();
              ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
              WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue);
              System.out.println(obj);
              System.out.println(weakReference.get());
              System.out.println(weakReference);
      
              obj = null;
              System.gc();
              Thread.sleep(500);
      
              System.out.println("GC以後....");
              System.out.println(obj);
              System.out.println(weakReference.get());
              System.out.println(weakReference);
          }
      }

      輸出

      1
      2
      3
      4
      5
      6
      7
      java.lang.Object@1540e19d
      java.lang.Object@1540e19d
      java.lang.ref.WeakReference@677327b6
      GC以後....
      null
      null
      java.lang.ref.WeakReference@677327b6

      會把該對象的包裝類即weakReference放入到ReferenceQueue裏面,咱們能夠從queue中獲取到相應的對象信息,同時進行額外的處理。好比反向操做,數據清理等。

  • 虛引用
    • 特色:虛引用也叫幻象引用,經過PhantomReference類來實現。沒法經過虛引用訪問對象的任何屬性或函數。幻象引用僅僅是提供了一種確保對象被 finalize 之後,作某些事情的機制。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中。
      ReferenceQueue queue = new ReferenceQueue ();
      PhantomReference pr = new PhantomReference (object, queue);
      程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起一些程序行動。
    • 應用場景:可用來跟蹤對象被垃圾回收器回收的活動,當一個虛引用關聯的對象被垃圾收集器回收以前會收到一條系統通知。

請談談你對 OOM 的認識?

  • java.lang.StackOverflowError

    • 在一個函數中調用本身就會產生這個錯誤
  • java.lang.OutOfMemoryError : Java heap space

    • new 一個很大對象
  • java.lang.OutOfMemoryError : GC overhead limit exceeded

    • 執行垃圾收集的時間比例太大, 有效的運算量過小,默認狀況下,,若是GC花費的時間超過 98%, 而且GC回收的內存少於 2%, JVM就會拋出這個錯誤。
  • java.lang.OutOfMemoryError : Direct buffer memory

    配置參數:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

    1
    2
    3
    4
    5
    6
    public class DirectBufferDemo {
        public static void main(String[] args) {
            System.out.println("maxDirectMemory : " + sun.misc.VM.maxDirectMemory() / (1024 * 1024) + "MB");
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
        }
    }

    輸出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    maxDirectMemory : 5MB
    [GC (System.gc()) [PSYoungGen: 1315K->464K(2560K)] 1315K->472K(9728K), 0.0008907 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [Full GC (System.gc()) [PSYoungGen: 464K->0K(2560K)] [ParOldGen: 8K->359K(7168K)] 472K->359K(9728K), [Metaspace: 3037K->3037K(1056768K)], 0.0060466 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    	at java.nio.Bits.reserveMemory(Bits.java:694)
    	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    	at com.cuzz.jvm.DirectBufferDemo.main(DirectBufferDemo.java:17)
    Heap
     PSYoungGen      total 2560K, used 56K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
      eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0e170,0x00000000fff00000)
      from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
      to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
     ParOldGen       total 7168K, used 359K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
      object space 7168K, 5% used [0x00000000ff600000,0x00000000ff659e28,0x00000000ffd00000)
     Metaspace       used 3068K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 336K, capacity 388K, committed 512K, reserved 1048576K
  • java.lang.OutOfMemoryError : unable to create new native thread

    • 建立線程數太多了
  • java.lang.OutOfMemoryError : Metaspace

    • Java 8 以後的版本使用元空間(Metaspace)代替了永久代,元空間是方法區在 HotSpot 中的實現,它與持久代最大的區別是:元空間並不在虛擬機中的內存中而是使用本地內存。
    • 元空間存放的信息:
      • 虛擬機加載的類信息
      • 常量池
      • 靜態變量
      • 即時編譯後的代碼

具體的實現能夠看看這個帖子:幾種手動OOM的方式

GC 垃圾回收算法和垃圾收集器的關係?談談你的理解?

  • 四種 GC 垃圾回收算法
    • 引用計數
    • 複製回收
    • 標記清除
    • 標記整理
  • GC 算法是內存回收的方法論,垃圾收集其就是算法的落實的實現。
  • 目前爲止尚未完美的收集器的出現,更加沒有萬能的收集器,只是針對具體應用最適合的收集器,進行分代收集。
  • 串行垃圾回收器(Serial)
    • 它爲單線程環境設計且只使用一個線程進行垃圾回收,會暫停全部的用戶線程,因此不適合服務環境。
  • 並行垃圾回收器(Parallel)
    • 多個垃圾收集線程並行工做,此時用戶線程是暫停的,用於科學計算、大數據處理等弱交互場景。
  • 併發垃圾回收器(CMS)
    • 用戶線程和垃圾收集線程同時執行(不必定是並行,多是交替執行),不須要停頓用戶線程,互聯網公司多用它,適用對相應時間有要求的場景。
  • G1 垃圾回收器
    • G1 垃圾回收器將堆內存分割成不一樣的區域而後併發的對其進行垃圾回收。

怎麼查看服務器默認垃圾收集器是哪一個?生產是如何配置垃圾收集器?談談你對垃圾收集器的理解?

  • 怎麼查看服務器默認垃圾收集器是哪一個?
    • Java -XX:+PrintCommandLineFlags
  • Java 的 GC 回收的類型主要有:
    • UseSerialGC,UseParallelGC,UseConcMarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC
    • Java 8 之後基本不使用 Serial Old
  • 垃圾收集器
    timg
  • 參數說明
    • DefNew : Default New Generation
    • Tenured : Old
    • ParNew : Parallel New Generation
    • PSYoungGen : Parallel Scavenge
    • ParOldGen : Parallel Old Generation
  • Server/Client 模式分別是什麼意思
    • 最主要的差異在於:-Server模式啓動時,速度較慢,可是一旦運行起來後,性能將會有很大的提高。
    • 當虛擬機運行在-client模式的時候,使用的是一個代號爲C1的輕量級編譯器, 而-server模式啓動的虛擬機採用相對重量級,代號爲C2的編譯器,C2比C1編譯器編譯的相對完全,服務起來以後,性能更高。
    • 因此一般用於作服務器的時候咱們用服務端模式,若是你的電腦只是運行一下java程序,就客戶端模式就能夠了。固然這些都是咱們作程序優化程序才須要這些東西的,普通人並不關注這些專業的東西了。其實服務器模式即便編譯更完全,而後垃圾回收優化更好,這固然吃的內存要多點相對於客戶端模式。
  • 新生代
    • 串行 GC (Serial/ Serital Copying)
    • 並行 GC (ParNew)
    • 並行回收 GC (Parallel/ Parallel Scanvenge)
  • 老年代

    • 串行 GC (Serial Old/ Serial MSC)
    • 並行 GC (Parallel Old/ Parallel MSC)
    • 併發標記清除 GC (CMS)
      • 是一種以獲取最短回收停頓時間爲目標的收集器,適合應用在互聯網站或者 B/S 系統的服務器上,這個類應用尤爲重視服務器的響應速度,但願系統停頓時間最短。
      • CMS 很是適合堆內存大、CPU 核數多的服務器端應用,也是 G1 出現以前大型應用首選收集器。
      • 併發停頓比較少,併發指的是與用戶線程一塊兒執行。
      • 過程
        • 初始標記(initail mark):只是標記一下 GC Roots 能直接關聯的對象,速度很快,須要暫停全部的工做線程
        • 併發標記(concurrent mark 和用戶線程一塊兒):進行 GC Roots 的跟蹤過程,和用戶線程一塊兒工做,不須要暫停工做線程。
        • 從新標記(remark):爲了修正在併發標記期間,因用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄,仍然須要暫停全部的工做線程。
        • 併發清除(concurrent sweep 和用戶線程一塊兒):清除 GC 不可達對象,和用戶線程一塊兒工做,不須要暫停工做線程,基於標記結果,直接清除。因爲耗時最長的併發標記和併發清除過程當中,垃圾收集線程和用戶線程能夠一塊兒併發工做,因此整體來看 CMS 收集器的內存回收和用戶線程是一塊兒併發地執行。
      • 優缺點
        • 優勢:併發收集停頓低
        • 缺點:併發執行對 CPU 資源壓力大,採用的標記清除算法會致使大量碎片
      • 因爲併發進行, CMS 在收集與應用線程會同時增長對堆內存的佔用,也就是說,CMS 必需要在老年代堆用盡以前完成垃圾回收,否者 CMS 回收失敗,將觸發擔保機制,串行老年代收集器將會以 STW 的方式進行一次 GC,從而形成較大的停頓時間。
      • 標記清除算法沒法整理空間碎片,老年代空間會隨着應用時長被逐漸耗盡,最後將不得不經過擔保機制對堆內存進行壓縮。CMS 也提供了參數 -XX:CMSFullGCsBeForeCompaction (默認0,即每次都進行內存整理) 來指定多少次 CMS 收集以後,進行一次壓
  • 垃圾收集器配置代碼總結

    • 配置新生代收集器,老年代收集器會自動配置上。
      1558237229584
  • 如何選擇垃圾收集器
    • 單 CPU 或者小內存,單機程序:-XX:UseSerialGC
    • 多 CPU 須要最大吞吐量,如後臺計算型應用:-XX:UseParallelGC 或者 -XX:UseParallelOldGC
    • 多 CPU 追求低停頓時間,須要快速響應,如互聯網應用:-XX:+UseConcMarkSweepGC

G1 垃圾收集器你瞭解嗎?

  • 之前收集器的特色
    • 年輕代和老年代是各自獨立且連續的內存塊
    • 年輕代收集器使用 eden + S0 + S1 進行復制算法
    • 老年代收集必須掃描整個老年代區域
    • 都是以儘量的少而快速地執行 GC 爲設計原則
  • G1 是什麼
    • G1 是一種面向服務端的垃圾收集器,應用在多核處理器和大容量內存環境中,在實現高吞吐量的同時,儘量的知足垃圾收集器的暫停時間要求。
    • 像 CMS 收集器同樣,能與應用程序線程併發執行,整理空閒空間更快,須要更多的時間來預測 GC 停頓時間,不但願犧牲大量的吞吐性能,不須要更大的 JAVA Heap。
    • G1 收集器的設計目的是取代 CMS 收集器,同時與 CMS 相比,G1 垃圾收集器是一個有整理內存過程的垃圾收集器,不會產生不少內存碎片。G1 的 Stop The World 更可控,G1 在停頓上添加了預測機制,用戶能夠指按期望的停頓時間。
    • G1 是在 2012 年纔在 jdk.1.7u4 中能夠呀用,在 jdk9 中將 G1 變成默認垃圾收集器來代替 CMS。它是以款面向服務應用的收集器。
    • 主要改變是 Eden、Survivor 和 Tenured 等內存區域再也不是連續的,而是變成了一個個大小同樣的 region,每一個 region 從 1M 到 32M 不等,一個 region 有可能屬於 Eden、Survivor 或者 Tenured 內存區域。
  • 特色
    • G1 能充分利用多 CPU、多核環境硬件優點,儘可能縮短 STW。
    • G1 總體採用標記-整理算法,局部是經過是經過複製算法,不會產生內存碎片
    • 宏觀上看 G1 之中不在區分年輕代和老年代,被內存劃分爲多個獨立的子區域。
    • G1 收集器裏面講整個的內存區域混合在一塊兒,但其自己依然在小範圍內要進行年輕代和老年代的區分。保留了新生代和老年代,但她們不在是物理隔離,而是一部分 Region 的集合且不須要 Region 是連續的,也就是說依然會採用不一樣的 GC 方式來處理不一樣的區域。
    • G1 雖然也是分代收集器,但整個內存分區不存在物理上的年輕代和老年代的區別,也不須要徹底獨立的 Survivor to space 堆作複製準備。G1 只有邏輯上的分代概念,或者說每一個分區均可能隨 G1 的運行在不一樣代之間先後切換。
  • 底層原理

    • Region 區域化垃圾收集器:最大好處是化整爲零,避免全內存掃描,只須要按照區域來進行掃描便可。

    • Region

      5611237-f643066bd97c7703
      G1的內存結構和傳統的內存空間劃分有比較的不一樣。G1將內存劃分紅了多個大小相等的Region(默認是512K),Region邏輯上連續,物理內存地址不連續。同時每一個Region被標記成E、S、O、H,分別表示Eden、Survivor、Old、Humongous。其中E、S屬於年輕代,O與H屬於老年代。
      H表示Humongous。從字面上就能夠理解表示大的對象(下面簡稱H對象)。當分配的對象大於等於Region大小的一半的時候就會被認爲是巨型對象。H對象默認分配在老年代,能夠防止GC的時候大對象的內存拷貝。經過若是發現堆內存容不下H對象的時候,會觸發一次GC操做。

    • 回收步驟
    • 四步過程
      u=1236259389,1737476709&fm=26&gp=0

生產環境服務器變慢,診斷思路和性能評估談談?

  • 整機:top
  • CPU:vmstat
  • 內存:free
  • 硬盤:df
  • 磁盤IO:iostat
  • 網絡IO:ifstat

假如生產環境出現 CPU 太高,請談談你的分析思路和定位?

  • 先用 top 命令找出 CPU 佔比最高的
  • ps -ef 或者 jps 進一步定位,得知是一個怎麼樣的一個後臺程序
  • 定位到具體的線程或代碼
    • ps -mp 11111 -o THREAD,tid,time
    • -m 顯示全部的線程
    • -p 進程使用cpu的時間
    • -o 該參數後是用戶自定義格式
  • 將須要的線程 ID 轉化爲 16 進制格式
  • jstat <進程ID> | grep <線程ID(16進制)> -A60

對於 JDK 自帶的 JVM 監控和性能分析工具用過哪些?通常機是怎麼用到的?

下一篇重點介紹。

參考連接

相關文章
相關標籤/搜索