国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

JVM常見(jiàn)問(wèn)題筆記分享

這篇具有很好參考價(jià)值的文章主要介紹了JVM常見(jiàn)問(wèn)題筆記分享。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

1 JVM組成

1.1 JVM由那些部分組成,運(yùn)行流程是什么?

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆☆

JVM是什么

Java Virtual Machine Java程序的運(yùn)行環(huán)境(java二進(jìn)制字節(jié)碼的運(yùn)行環(huán)境)

好處:

  • 一次編寫(xiě),到處運(yùn)行
  • 自動(dòng)內(nèi)存管理,垃圾回收機(jī)制

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

JVM由哪些部分組成,運(yùn)行流程是什么?

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

從圖中可以看出 JVM 的主要組成部分

  • ClassLoader(類(lèi)加載器)
  • Runtime Data Area(運(yùn)行時(shí)數(shù)據(jù)區(qū),內(nèi)存分區(qū))
  • Execution Engine(執(zhí)行引擎)
  • Native Method Library(本地庫(kù)接口)

運(yùn)行流程:

(1)類(lèi)加載器(ClassLoader)把Java代碼轉(zhuǎn)換為字節(jié)碼

(2)運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)把字節(jié)碼加載到內(nèi)存中,而字節(jié)碼文件只是JVM的一套指令集規(guī)范,并不能直接交給底層系統(tǒng)去執(zhí)行,而是有執(zhí)行引擎運(yùn)行

(3)執(zhí)行引擎(Execution Engine)將字節(jié)碼翻譯為底層系統(tǒng)指令,再交由CPU執(zhí)行去執(zhí)行,此時(shí)需要調(diào)用其他語(yǔ)言的本地庫(kù)接口(Native Method Library)來(lái)實(shí)現(xiàn)整個(gè)程序的功能。

1.2 什么是程序計(jì)數(shù)器?

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆☆

程序計(jì)數(shù)器:線(xiàn)程私有的(每一個(gè)線(xiàn)程內(nèi)部都有一個(gè)程序計(jì)數(shù)器),內(nèi)部保存的字節(jié)碼的行號(hào)。用于記錄正在執(zhí)行的字節(jié)碼指令的地址。

javap -verbose xx.class 打印堆棧大小,局部變量的數(shù)量和方法的參數(shù)。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

java虛擬機(jī)對(duì)于多線(xiàn)程是通過(guò)線(xiàn)程輪流切換并且分配線(xiàn)程執(zhí)行時(shí)間。在任何的一個(gè)時(shí)間點(diǎn)上,一個(gè)處理器只會(huì)處理執(zhí)行一個(gè)線(xiàn)程,如果當(dāng)前被執(zhí)行的這個(gè)線(xiàn)程它所分配的執(zhí)行時(shí)間用完了【掛起】。處理器會(huì)切換到另外的一個(gè)線(xiàn)程上來(lái)進(jìn)行執(zhí)行。并且這個(gè)線(xiàn)程的執(zhí)行時(shí)間用完了,接著處理器就會(huì)又來(lái)執(zhí)行被掛起的這個(gè)線(xiàn)程。

那么現(xiàn)在有一個(gè)問(wèn)題就是,當(dāng)前處理器如何能夠知道,對(duì)于這個(gè)被掛起的線(xiàn)程,它上一次執(zhí)行到了哪里?那么這時(shí)就需要從程序計(jì)數(shù)器中來(lái)回去到當(dāng)前的這個(gè)線(xiàn)程他上一次執(zhí)行的行號(hào),然后接著繼續(xù)向下執(zhí)行。

程序計(jì)數(shù)器是JVM規(guī)范中唯一一個(gè)沒(méi)有規(guī)定出現(xiàn)OOM的區(qū)域,所以這個(gè)空間也不會(huì)進(jìn)行GC。

1.3 你能給我詳細(xì)的介紹Java堆嗎?

Java堆事線(xiàn)程共享的區(qū)域,主要用來(lái)保存實(shí)例、數(shù)組等, 當(dāng)堆中沒(méi)有內(nèi)存空間可分配給實(shí)例,也無(wú)法擴(kuò)展的時(shí)候,會(huì)拋出OOM異常。

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆☆

線(xiàn)程共享的區(qū)域:主要用來(lái)保存對(duì)象實(shí)例,數(shù)組等,當(dāng)堆中沒(méi)有內(nèi)存空間可分配給實(shí)例,也無(wú)法再擴(kuò)展時(shí),則拋出OutOfMemoryError異常。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 年輕代被劃分為三部分,Eden區(qū)和兩個(gè)大小嚴(yán)格相同的Survivor區(qū),根據(jù)JVM的策略,在經(jīng)過(guò)幾次垃圾收集后,任然存活于Survivor的對(duì)象將被移動(dòng)到老年代區(qū)間。
  • 老年代主要保存生命周期長(zhǎng)的對(duì)象,一般是一些老的對(duì)象
  • 元空間保存的類(lèi)信息、靜態(tài)變量、常量、編譯后的代碼

為了避免方法區(qū)出現(xiàn)OOM,所以在java8中將堆上的方法區(qū)【永久代】給移動(dòng)到了本地內(nèi)存上,重新開(kāi)辟了一塊空間,叫做元空間。那么現(xiàn)在就可以避免掉OOM的出現(xiàn)了。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

元空間(MetaSpace)介紹
在 HotSpot JVM 中,永久代( ≈ 方法區(qū))中用于存放類(lèi)和方法的元數(shù)據(jù)以及常量池,比如Class 和 Method。每當(dāng)一個(gè)類(lèi)初次被加載的時(shí)候,它的元數(shù)據(jù)都會(huì)放到永久代中。

永久代是有大小限制的,因此如果加載的類(lèi)太多,很有可能導(dǎo)致永久代內(nèi)存溢出,即OutOfMemoryError,為此不得不對(duì)虛擬機(jī)做調(diào)優(yōu)。

那么,Java 8 中 PermGen 為什么被移出 HotSpot JVM 了?

官網(wǎng)給出了解釋?zhuān)篽ttp://openjdk.java.net/jeps/122

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

移除永久代是為融合HotSpot JVM與 JRockit VM而做出的努力,因?yàn)镴Rockit沒(méi)有永久代,不需要配置永久代。

1)由于 PermGen 內(nèi)存經(jīng)常會(huì)溢出,引發(fā)OutOfMemoryError,因此 JVM 的開(kāi)發(fā)者希望這一塊內(nèi)存可以更靈活地被管理,不要再經(jīng)常出現(xiàn)這樣的 OOM。

2)移除 PermGen 可以促進(jìn) HotSpot JVM 與 JRockit VM 的融合,因?yàn)?JRockit 沒(méi)有永久代。

準(zhǔn)確來(lái)說(shuō),Perm 區(qū)中的字符串常量池被移到了堆內(nèi)存中是在 Java7 之后,Java 8 時(shí),PermGen 被元空間代替,其他內(nèi)容比如**類(lèi)元信息、字段、靜態(tài)屬性、方法、常量**等都移動(dòng)到元空間區(qū)。比如 java/lang/Object 類(lèi)元信息、靜態(tài)屬性 System.out、整型常量等。

元空間的本質(zhì)和永久代類(lèi)似,都是對(duì) JVM 規(guī)范中方法區(qū)的實(shí)現(xiàn)。不過(guò)元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存。因此,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制。

1.4 什么是虛擬機(jī)棧

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆☆

Java Virtual machine Stacks (java 虛擬機(jī)棧)

  • 每個(gè)線(xiàn)程運(yùn)行時(shí)所需要的內(nèi)存,稱(chēng)為虛擬機(jī)棧,先進(jìn)后出
  • 每個(gè)棧由多個(gè)棧幀(frame)組成,對(duì)應(yīng)著每次方法調(diào)用時(shí)所占用的內(nèi)存
  • 每個(gè)線(xiàn)程只能有一個(gè)活動(dòng)棧幀,對(duì)應(yīng)著當(dāng)前正在執(zhí)行的那個(gè)方法

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  1. 垃圾回收是否涉及棧內(nèi)存?
    垃圾回收主要指就是堆內(nèi)存,當(dāng)棧幀彈棧以后,內(nèi)存就會(huì)釋放
  2. 棧內(nèi)存分配越大越好嗎?
    未必,默認(rèn)的棧內(nèi)存通常為1024k(1M)
    棧幀過(guò)大會(huì)導(dǎo)致線(xiàn)程數(shù)變少,例如,機(jī)器總內(nèi)存為512m,目前能活動(dòng)的線(xiàn)程數(shù)則為512個(gè),如果把棧內(nèi)存改為2048k,那么能活動(dòng)的棧幀就會(huì)減半
  3. **方法內(nèi)的局部變量是否線(xiàn)程安全? **
    • 如果方法內(nèi)局部變量沒(méi)有逃離方法的作用范圍,它是線(xiàn)程安全的
    • 如果是局部變量引用了對(duì)象,并逃離方法的作用范圍,需要考慮線(xiàn)程安全
    • 比如以下代碼:
      JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

棧內(nèi)存溢出情況

  • 棧幀過(guò)多導(dǎo)致棧內(nèi)存溢出,典型問(wèn)題:遞歸調(diào)用
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
  • 棧幀過(guò)大導(dǎo)致棧內(nèi)存溢出

1.5 堆和棧的區(qū)別

難易程度:☆☆☆
方面:線(xiàn)程是否私有, 異常, 處理的問(wèn)題
出現(xiàn)頻率:☆☆☆

堆主要是解決實(shí)例管理的問(wèn)題,
棧解決的是程序運(yùn)行的問(wèn)題
本地方法棧與棧功能相同,是一個(gè)Java調(diào)用接口調(diào)用非Java的代碼

  • 棧內(nèi)存一般會(huì)用來(lái)存儲(chǔ)局部變量和方法調(diào)用,但堆內(nèi)存是用來(lái)存儲(chǔ)Java對(duì)象和數(shù)組的的。堆會(huì)GC垃圾回收,而棧不會(huì)。
  • 棧內(nèi)存是線(xiàn)程私有的,而堆內(nèi)存是線(xiàn)程共有的。
  • 兩者異常錯(cuò)誤不同,但如果棧內(nèi)存或者堆內(nèi)存不足都會(huì)拋出異常。
    ??臻g不足:java.lang.StackOverFlowError。
    堆空間不足:java.lang.OutOfMemoryEr

組成部分:堆、方法區(qū)、棧、本地方法棧、程序計(jì)數(shù)器

1、堆解決的是對(duì)象實(shí)例存儲(chǔ)的問(wèn)題,垃圾回收器管理的主要區(qū)域。
2、方法區(qū)可以認(rèn)為是堆的一部分,用于存儲(chǔ)已被虛擬機(jī)加載的信息,常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼。
3、棧解決的是程序運(yùn)行的問(wèn)題,棧里面存的是棧幀,棧幀里面存的是局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。
4、本地方法棧與棧功能相同,本地方法棧執(zhí)行的是本地方法,一個(gè)Java調(diào)用非Java代碼的接口。
5、程序計(jì)數(shù)器(PC寄存器)程序計(jì)數(shù)器中存放的是當(dāng)前線(xiàn)程所執(zhí)行的字節(jié)碼的行數(shù)。JVM工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一個(gè)需要執(zhí)行的字節(jié)碼指令。

1.6 能不能解釋一下方法區(qū)?

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆

1.5.1 概述
  • 方法區(qū)(Method Area)是各個(gè)線(xiàn)程共享的內(nèi)存區(qū)域
  • 主要存儲(chǔ)類(lèi)的信息、運(yùn)行時(shí)常量池
  • 虛擬機(jī)啟動(dòng)的時(shí)候創(chuàng)建,關(guān)閉虛擬機(jī)時(shí)釋放
  • 如果方法區(qū)域中的內(nèi)存無(wú)法滿(mǎn)足分配請(qǐng)求,則會(huì)拋出OutOfMemoryError: Metaspace

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

1.5.2 常量池

可以看作是一張表,虛擬機(jī)指令根據(jù)這張常量表找到要執(zhí)行的類(lèi)名、方法名、參數(shù)類(lèi)型、字面量等信息

查看字節(jié)碼結(jié)構(gòu)(類(lèi)的基本信息、常量池、方法定義)javap -v xx.class

比如下面是一個(gè)Application類(lèi)的main方法執(zhí)行,源碼如下:

public class Application {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

找到類(lèi)對(duì)應(yīng)的class文件存放目錄,執(zhí)行命令:javap -v Application.class 查看字節(jié)碼結(jié)構(gòu)

D:\code\jvm-demo\target\classes\com\heima\jvm>javap -v Application.class
Classfile /D:/code/jvm-demo/target/classes/com/heima/jvm/Application.class
  Last modified 2023-05-07; size 564 bytes    //最后修改的時(shí)間
  MD5 checksum c1b64ed6491b9a16c2baab5061c64f88   //簽名
  Compiled from "Application.java"   //從哪個(gè)源碼編譯
public class com.heima.jvm.Application   //包名,類(lèi)名
  minor version: 0
  major version: 52     //jdk版本	
  flags: ACC_PUBLIC, ACC_SUPER  //修飾符
Constant pool:   //常量池
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #23            // hello world
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // com/heima/jvm/Application
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/heima/jvm/Application;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               Application.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8               hello world
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               com/heima/jvm/Application
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  public com.heima.jvm.Application();  //構(gòu)造方法
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/heima/jvm/Application;

  public static void main(java.lang.String[]);  //main方法
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String hello world
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 7: 0
        line 8: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}
SourceFile: "Application.java"

下圖,左側(cè)是main方法的指令信息,右側(cè)constant pool 是常量池

main方法按照指令執(zhí)行的時(shí)候,需要到常量池中查表翻譯找到具體的類(lèi)和方法地址去執(zhí)行

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

1.5.3 運(yùn)行時(shí)常量池

常量池是 *.class 文件中的,當(dāng)該類(lèi)被加載,它的常量池信息就會(huì)放入運(yùn)行時(shí)常量池,并把里面的符號(hào)地址變?yōu)檎鎸?shí)地址

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

1.7 你聽(tīng)過(guò)直接內(nèi)存嗎?

  • 并不屬于JVM中的內(nèi)存結(jié)構(gòu),不由JVM進(jìn)行管理。是虛擬機(jī)的系統(tǒng)內(nèi)存
  • 常見(jiàn)于 NIO 操作時(shí),用于數(shù)據(jù)緩沖區(qū),分配回收成本較高,但讀寫(xiě)性能高(減少了從系統(tǒng)內(nèi)存到Java堆內(nèi)存的拷貝),不受JVM內(nèi)存回收管理

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆

不受 JVM 內(nèi)存回收管理,是虛擬機(jī)的系統(tǒng)內(nèi)存,常見(jiàn)于 NIO 操作時(shí),用于數(shù)據(jù)緩沖區(qū),分配回收成本較高,但讀寫(xiě)性能高,不受 JVM 內(nèi)存回收管理

舉例:

需求,在本地電腦中的一個(gè)較大的文件(超過(guò) 100m)從一個(gè)磁盤(pán)挪到另外一個(gè)磁盤(pán)

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

代碼如下:

/**
 * 演示 ByteBuffer 作用
 */
public class Demo1_9 {
    static final String FROM = "E:\\編程資料\\第三方教學(xué)視頻\\youtube\\Getting Started with Spring Boot-sbPSjI4tt10.mp4";
    static final String TO = "E:\\a.mp4";
    static final int _1Mb = 1024 * 1024;

    public static void main(String[] args) {
        io(); // io 用時(shí):1535.586957 1766.963399 1359.240226
        directBuffer(); // directBuffer 用時(shí):479.295165 702.291454 562.56592
    }

    private static void directBuffer() {
        long start = System.nanoTime();
        try (FileChannel from = new FileInputStream(FROM).getChannel();
             FileChannel to = new FileOutputStream(TO).getChannel();
        ) {
            ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb);
            while (true) {
                int len = from.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                to.write(bb);
                bb.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("directBuffer 用時(shí):" + (end - start) / 1000_000.0);
    }

    private static void io() {
        long start = System.nanoTime();
        try (FileInputStream from = new FileInputStream(FROM);
             FileOutputStream to = new FileOutputStream(TO);
        ) {
            byte[] buf = new byte[_1Mb];
            while (true) {
                int len = from.read(buf);
                if (len == -1) {
                    break;
                }
                to.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("io 用時(shí):" + (end - start) / 1000_000.0);
    }
}

io用時(shí):182.7593
directBuffer用時(shí):
98.4438T

可以發(fā)現(xiàn),使用傳統(tǒng)的IO的時(shí)間要比NIO操作的時(shí)間長(zhǎng)了很多了,也就說(shuō)NIO的讀性能更好。

這個(gè)是跟我們的JVM的直接內(nèi)存是有一定關(guān)系,如下圖,是傳統(tǒng)阻塞IO的數(shù)據(jù)傳輸流程

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

下圖是NIO傳輸數(shù)據(jù)的流程,在這個(gè)里面主要使用到了一個(gè)直接內(nèi)存,不需要在堆中開(kāi)辟空間進(jìn)行數(shù)據(jù)的拷貝,jvm可以直接操作直接內(nèi)存,從而使數(shù)據(jù)讀寫(xiě)傳輸更快。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

1.8 堆棧的區(qū)別是什么?

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆☆

1、棧內(nèi)存一般會(huì)用來(lái)存儲(chǔ)局部變量和方法調(diào)用,但堆內(nèi)存是用來(lái)存儲(chǔ)Java對(duì)象和數(shù)組的的。堆會(huì)GC垃圾回收,而棧不會(huì)。

2、棧內(nèi)存是線(xiàn)程私有的,而堆內(nèi)存是線(xiàn)程共有的。

3,、兩者異常錯(cuò)誤不同,但如果棧內(nèi)存或者堆內(nèi)存不足都會(huì)拋出異常。

棧空間不足:java.lang.StackOverFlowError。

堆空間不足:java.lang.OutOfMemoryError。

2 類(lèi)加載器

2.1 什么是類(lèi)加載器,類(lèi)加載器有哪些?

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆

要想理解類(lèi)加載器的話(huà),務(wù)必要先清楚對(duì)于一個(gè)Java文件,它從編譯到執(zhí)行的整個(gè)過(guò)程。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 類(lèi)加載器:用于裝載字節(jié)碼文件(.class文件)
  • 運(yùn)行時(shí)數(shù)據(jù)區(qū):用于分配存儲(chǔ)空間
  • 執(zhí)行引擎:執(zhí)行字節(jié)碼文件或本地方法
  • 垃圾回收器:用于對(duì)JVM中的垃圾內(nèi)容進(jìn)行回收

類(lèi)加載器

JVM只會(huì)運(yùn)行二進(jìn)制文件,而類(lèi)加載器(ClassLoader)的主要作用就是將字節(jié)碼文件加載到JVM中,從而讓Java程序能夠啟動(dòng)起來(lái)。現(xiàn)有的類(lèi)加載器基本上都是java.lang.ClassLoader的子類(lèi),該類(lèi)的主要職責(zé)就是用于將指定的類(lèi)找到或生成對(duì)應(yīng)的字節(jié)碼文件,同時(shí)類(lèi)加載器還會(huì)負(fù)責(zé)加載程序所需要的資源

類(lèi)加載器種類(lèi)

類(lèi)加載器根據(jù)各自加載范圍的不同,劃分為四種類(lèi)加載器:

  • 啟動(dòng)類(lèi)加載器(BootStrap ClassLoader):
    該類(lèi)并不繼承ClassLoader類(lèi),其是由C++編寫(xiě)實(shí)現(xiàn)。用于加載JAVA_HOME/jre/lib目錄下的類(lèi)庫(kù)。
  • 擴(kuò)展類(lèi)加載器(ExtClassLoader):
    該類(lèi)是ClassLoader的子類(lèi),主要加載JAVA_HOME/jre/lib/ext目錄中的類(lèi)庫(kù)。
  • 應(yīng)用類(lèi)加載器(AppClassLoader):
    該類(lèi)是ClassLoader的子類(lèi),主要用于加載classPath下的類(lèi),也就是加載開(kāi)發(fā)者自己編寫(xiě)的Java類(lèi)。
  • 自定義類(lèi)加載器:
    開(kāi)發(fā)者自定義類(lèi)繼承ClassLoader,實(shí)現(xiàn)自定義類(lèi)加載規(guī)則。

上述三種類(lèi)加載器的層次結(jié)構(gòu)如下如下:

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

類(lèi)加載器的體系并不是“繼承”體系,而是委派體系,類(lèi)加載器首先會(huì)到自己的parent中查找類(lèi)或者資源,如果找不到才會(huì)到自己本地查找。類(lèi)加載器的委托行為動(dòng)機(jī)是為了避免相同的類(lèi)被加載多次。

2.2 什么是雙親委派模型?

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆☆

如果一個(gè)類(lèi)加載器在接到加載類(lèi)的請(qǐng)求時(shí),它首先不會(huì)自己嘗試去加載這個(gè)類(lèi),而是把這個(gè)請(qǐng)求任務(wù)委托給父類(lèi)加載器去完成,**依次遞歸,**如果父類(lèi)加載器可以完成類(lèi)加載任務(wù),就返回成功;只有父類(lèi)加載器無(wú)法完成此加載任務(wù)時(shí),才由下一級(jí)去加載。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

2.3 JVM為什么采用雙親委派機(jī)制

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆

(1)通過(guò)雙親委派機(jī)制可以避免某一個(gè)類(lèi)被重復(fù)加載,當(dāng)父類(lèi)已經(jīng)加載后則無(wú)需重復(fù)加載,保證唯一性。

(2)為了安全,保證類(lèi)庫(kù)API不會(huì)被修改

在工程中新建java.lang包,接著在該包下新建String類(lèi),并定義main函數(shù)

public class String {

    public static void main(String[] args) {

        System.out.println("demo info");
    }
}
此時(shí)執(zhí)行main函數(shù),會(huì)出現(xiàn)異常,在類(lèi) java.lang.String 中找不到 main 方法

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

出現(xiàn)該信息是因?yàn)橛呻p親委派的機(jī)制,java.lang.String的在啟動(dòng)類(lèi)加載器(Bootstrap classLoader)得到加載,因?yàn)樵诤诵膉re庫(kù)中有其相同名字的類(lèi)文件,但該類(lèi)中并沒(méi)有main方法。這樣就能**防止惡意篡改核心API庫(kù)**。

2.4 說(shuō)一下類(lèi)裝載的執(zhí)行過(guò)程?

難易程度:☆☆☆☆☆

出現(xiàn)頻率:☆☆☆

類(lèi)從加載到虛擬機(jī)中開(kāi)始,直到卸載為止,它的整個(gè)生命周期包括了:加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用和卸載這7個(gè)階段。其中,驗(yàn)證、準(zhǔn)備和解析這三個(gè)部分統(tǒng)稱(chēng)為連接(linking)。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

類(lèi)加載過(guò)程詳解

1.加載

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 通過(guò)類(lèi)的全名,獲取類(lèi)的**二進(jìn)制數(shù)據(jù)流。 **
  • 解析類(lèi)的二進(jìn)制數(shù)據(jù)流為方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)(Java類(lèi)模型)
  • 創(chuàng)建java.lang.Class類(lèi)的實(shí)例,表示該類(lèi)型。作為方法區(qū)這個(gè)類(lèi)的各種數(shù)據(jù)的訪(fǎng)問(wèn)入口

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

2.驗(yàn)證

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

驗(yàn)證類(lèi)是否符合JVM規(guī)范,安全性檢查

(1)文件格式驗(yàn)證:是否符合Class文件的規(guī)范
(2)元數(shù)據(jù)驗(yàn)證
這個(gè)類(lèi)是否有父類(lèi)(除了Object這個(gè)類(lèi)之外,其余的類(lèi)都應(yīng)該有父類(lèi))
這個(gè)類(lèi)是否繼承(extends)了被final修飾過(guò)的類(lèi)(被final修飾過(guò)的類(lèi)表示類(lèi)不能被繼承)
類(lèi)中的字段、方法是否與父類(lèi)產(chǎn)生矛盾。(被final修飾過(guò)的方法或字段是不能覆蓋的)
(3)字節(jié)碼驗(yàn)證
主要的目的是通過(guò)對(duì)數(shù)據(jù)流和控制流的分析,確定程序語(yǔ)義是合法的、符合邏輯的。
(4)符號(hào)引用驗(yàn)證:符號(hào)引用以一組符號(hào)來(lái)描述所引用的目標(biāo)(判斷是否存在),符號(hào)可以是任何形式的字面量

比如:int i = 3;
字面量:3
符號(hào)引用:i

3.準(zhǔn)備

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

為類(lèi)變量分配內(nèi)存并設(shè)置類(lèi)變量初始值

  • static變量,分配空間在準(zhǔn)備階段完成(設(shè)置默認(rèn)值),賦值在初始化階段完成
  • static變量是final的基本類(lèi)型,以及字符串常量,值已確定,賦值在準(zhǔn)備階段完成
  • static變量是final的引用類(lèi)型,那么賦值也會(huì)在初始化階段完成

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4.解析

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

把類(lèi)中的符號(hào)引用轉(zhuǎn)換為直接引用

比如:方法中調(diào)用了其他方法,方法名可以理解為符號(hào)引用,而直接引用就是使用指針直接指向方法。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

5.初始化

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

對(duì)類(lèi)的靜態(tài)變量,靜態(tài)代碼塊執(zhí)行初始化操作
前面的準(zhǔn)備階段做的是復(fù)制默認(rèn)的便利,比如0,null,
這個(gè)階段則是賦值變量所期待的值

  • 如果初始化一個(gè)類(lèi)的時(shí)候,其父類(lèi)尚未初始化,則優(yōu)先初始化其父類(lèi)。
  • 如果同時(shí)包含多個(gè)靜態(tài)變量和靜態(tài)代碼塊,則按照自上而下的順序依次執(zhí)行。

6.使用

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

JVM 開(kāi)始從入口方法開(kāi)始執(zhí)行用戶(hù)的程序代碼

  • 調(diào)用靜態(tài)類(lèi)成員信息(比如:靜態(tài)字段、靜態(tài)方法)
  • 使用new關(guān)鍵字為其創(chuàng)建對(duì)象實(shí)例

7.卸載

當(dāng)用戶(hù)程序代碼執(zhí)行完畢后,JVM 便開(kāi)始銷(xiāo)毀創(chuàng)建的 Class 對(duì)象,最后負(fù)責(zé)運(yùn)行的 JVM 也退出內(nèi)存

3 垃圾收回

3.1 簡(jiǎn)述Java垃圾回收機(jī)制?(GC是什么?為什么要GC)

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆

為了讓程序員更專(zhuān)注于代碼的實(shí)現(xiàn),而不用過(guò)多的考慮內(nèi)存釋放的問(wèn)題,所以,在Java語(yǔ)言中,有了自動(dòng)的垃圾回收機(jī)制,也就是我們熟悉的GC(Garbage Collection)。

有了垃圾回收機(jī)制后,程序員只需要關(guān)心內(nèi)存的申請(qǐng)即可,內(nèi)存的釋放由系統(tǒng)自動(dòng)識(shí)別完成。

在進(jìn)行垃圾回收時(shí),不同的對(duì)象引用類(lèi)型,GC會(huì)采用不同的回收時(shí)機(jī)

換句話(huà)說(shuō),自動(dòng)的垃圾回收的算法就會(huì)變得非常重要了,如果因?yàn)樗惴ǖ牟缓侠?,?dǎo)致內(nèi)存資源一直沒(méi)有釋放,同樣也可能會(huì)導(dǎo)致內(nèi)存溢出的。

當(dāng)然,除了Java語(yǔ)言,C#、Python等語(yǔ)言也都有自動(dòng)的垃圾回收機(jī)制。

3.2 對(duì)象什么時(shí)候可以被垃圾器回收

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆☆

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

簡(jiǎn)單一句就是:如果一個(gè)或多個(gè)對(duì)象沒(méi)有任何的引用指向它了,那么這個(gè)對(duì)象現(xiàn)在就是垃圾,如果定位了垃圾,則有可能會(huì)被垃圾回收器回收。

如果要定位什么是垃圾,有兩種方式來(lái)確定,第一個(gè)是引用計(jì)數(shù)法,第二個(gè)是可達(dá)性分析算法

3.2.1 引用計(jì)數(shù)法

一個(gè)對(duì)象被引用了一次,在當(dāng)前的對(duì)象頭上遞增一次引用次數(shù),如果這個(gè)對(duì)象的引用次數(shù)為0,代表這個(gè)對(duì)象可回收

String demo = new String("123");

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

String demo = null;

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

當(dāng)對(duì)象間出現(xiàn)了循環(huán)引用的話(huà),則引用計(jì)數(shù)法就會(huì)失效

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

先執(zhí)行右側(cè)代碼的前4行代碼

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

目前上方的引用關(guān)系和計(jì)數(shù)都是沒(méi)問(wèn)題的,但是,如果代碼繼續(xù)往下執(zhí)行,如下圖

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

雖然a和b都為null,但是由于a和b存在循環(huán)引用,這樣a和b永遠(yuǎn)都不會(huì)被回收。

優(yōu)點(diǎn):

  • 實(shí)時(shí)性較高,無(wú)需等到內(nèi)存不夠的時(shí)候,才開(kāi)始回收,運(yùn)行時(shí)根據(jù)對(duì)象的計(jì)數(shù)器是否為0,就可以直接回收。
  • 在垃圾回收過(guò)程中,應(yīng)用無(wú)需掛起。如果申請(qǐng)內(nèi)存時(shí),內(nèi)存不足,則立刻報(bào)OOM錯(cuò)誤。
  • 區(qū)域性,更新對(duì)象的計(jì)數(shù)器時(shí),只是影響到該對(duì)象,不會(huì)掃描全部對(duì)象。

缺點(diǎn):

  • 每次對(duì)象被引用時(shí),都需要去更新計(jì)數(shù)器,有一點(diǎn)時(shí)間開(kāi)銷(xiāo)。
  • 浪費(fèi)CPU資源,即使內(nèi)存夠用,仍然在運(yùn)行時(shí)進(jìn)行計(jì)數(shù)器的統(tǒng)計(jì)。
  • 無(wú)法解決循環(huán)引用問(wèn)題,會(huì)引發(fā)內(nèi)存泄露。(最大的缺點(diǎn))
3.2.2 可達(dá)性分析算法
現(xiàn)在的虛擬機(jī)采用的都是通過(guò)可達(dá)性分析算法來(lái)確定哪些內(nèi)容是垃圾。

會(huì)存在一個(gè)根節(jié)點(diǎn)【GC Roots】,引出它下面指向的下一個(gè)節(jié)點(diǎn),再以下一個(gè)節(jié)點(diǎn)節(jié)點(diǎn)開(kāi)始找出它下面的節(jié)點(diǎn),依次往下類(lèi)推。直到所有的節(jié)點(diǎn)全部遍歷完畢。

根對(duì)象是那些肯定不能當(dāng)做垃圾回收的對(duì)象,就可以當(dāng)做根對(duì)象

局部變量,靜態(tài)方法,靜態(tài)變量,類(lèi)信息

核心是:判斷某對(duì)象是否與根對(duì)象有直接或間接的引用,如果沒(méi)有被引用,則可以當(dāng)做垃圾回收

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

X,Y這兩個(gè)節(jié)點(diǎn)是可回收的,但是**并不會(huì)馬上的被回收??!** 對(duì)象中存在一個(gè)方法【finalize】。當(dāng)對(duì)象被標(biāo)記為可回收后,當(dāng)發(fā)生GC時(shí),首先**會(huì)判斷這個(gè)對(duì)象是否執(zhí)行了finalize方法**,如果這個(gè)方法還沒(méi)有被執(zhí)行的話(huà),那么就會(huì)先來(lái)執(zhí)行這個(gè)方法,接著在這個(gè)方法執(zhí)行中,可以設(shè)置當(dāng)前這個(gè)對(duì)象與GC ROOTS產(chǎn)生關(guān)聯(lián),那么這個(gè)方法執(zhí)行完成之后,GC會(huì)再次判斷對(duì)象是否可達(dá),如果仍然不可達(dá),則會(huì)進(jìn)行回收,如果可達(dá)了,則不會(huì)進(jìn)行回收。

finalize方法對(duì)于每一個(gè)對(duì)象來(lái)說(shuō),只會(huì)執(zhí)行一次。如果第一次執(zhí)行這個(gè)方法的時(shí)候,設(shè)置了當(dāng)前對(duì)象與RC ROOTS關(guān)聯(lián),那么這一次不會(huì)進(jìn)行回收。 那么等到這個(gè)對(duì)象第二次被標(biāo)記為可回收時(shí),那么該對(duì)象的finalize方法就不會(huì)再次執(zhí)行了。

GC ROOTS:

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象

“局部變量的引用存儲(chǔ)在虛擬機(jī)棧中,而對(duì)象存儲(chǔ)在堆中?!?/p>

/**
 * demo是棧幀中的本地變量,當(dāng) demo = null 時(shí),由于此時(shí) demo 充當(dāng)了 GC Root 的作用,demo與原來(lái)指向的實(shí)例 new Demo() 斷開(kāi)了連接,對(duì)象被回收。
 */
public class Demo {
    public static  void main(String[] args) {
    	Demo demo = new Demo();
    	demo = null;
    }
}
  • 方法區(qū)中類(lèi)靜態(tài)屬性引用的對(duì)象

“靜態(tài)變量的引用通常存儲(chǔ)在元空間,而對(duì)象仍然存儲(chǔ)在堆中?!?/p>

/**
 * 當(dāng)棧幀中的本地變量 b = null 時(shí),
 * 由于 b 原來(lái)指向的對(duì)象與 GC Root (變量 b) 斷開(kāi)了連接,
 * 所以 b 原來(lái)指向的對(duì)象會(huì)被回收,而由于我們給 a 賦值了變量的引用,
 * a在此時(shí)是類(lèi)靜態(tài)屬性引用,充當(dāng)了 GC Root 的作用,
 * 它指向的對(duì)象依然存活!
 */
public class Demo {
    public static Demo a;
    public static  void main(String[] args) {
        Demo b = new Demo();
        b.a = new Demo();
        b = null;
    }
}
  • 方法區(qū)中常量引用的對(duì)象

常量和這里的demo沒(méi)有什么關(guān)系,所以不會(huì)因?yàn)閐emo指向的對(duì)象被回收而回收

/**
 * 常量 a 指向的對(duì)象并不會(huì)因?yàn)?demo 指向的對(duì)象被回收而回收
 */
public class Demo {
    
    public static final Demo a = new Demo();
    
    public static  void main(String[] args) {
        Demo demo = new Demo();
        demo = null;
    }
}
  • 本地方法棧中 JNI(即一般說(shuō)的 Native 方法)引用的對(duì)象

“ // 只要 nativeMethod 還持有它的引用,它就不會(huì)被垃圾回收?!?/p>

3.3 JVM 垃圾回收算法有哪些?

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆☆

3.3.1 標(biāo)記清除算法

標(biāo)記清除算法,是將垃圾回收分為2個(gè)階段,分別是標(biāo)記和清除。

1.根據(jù)可達(dá)性分析算法得出的垃圾進(jìn)行標(biāo)記

2.對(duì)這些標(biāo)記為可回收的內(nèi)容進(jìn)行垃圾回收

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

可以看到,標(biāo)記清除算法解決了引用計(jì)數(shù)算法中的循環(huán)引用的問(wèn)題,沒(méi)有從root節(jié)點(diǎn)引用的對(duì)象都會(huì)被回收。

同樣,標(biāo)記清除算法也是有缺點(diǎn)的:

  • 效率較低,標(biāo)記和清除兩個(gè)動(dòng)作都需要遍歷所有的對(duì)象,并且在GC時(shí),需要停止應(yīng)用程序,對(duì)于交互性要求比較高的應(yīng)用而言這個(gè)體驗(yàn)是非常差的。
  • 重要)通過(guò)標(biāo)記清除算法清理出來(lái)的內(nèi)存,碎片化較為嚴(yán)重,因?yàn)楸换厥盏膶?duì)象可能存在于內(nèi)存的各個(gè)角落,所以清理出來(lái)的內(nèi)存是不連貫的。
3.3.2 復(fù)制算法
復(fù)制算法的核心就是,**將原有的內(nèi)存空間一分為二,每次只用其中的一塊**,在垃圾回收時(shí),將正在使用的對(duì)象復(fù)制到另一個(gè)內(nèi)存空間中,然后將該內(nèi)存空間清空,交換兩個(gè)內(nèi)存的角色,完成垃圾的回收。

如果內(nèi)存中的垃圾對(duì)象較多,需要復(fù)制的對(duì)象就較少,這種情況下適合使用該方式并且效率比較高,反之,則不適合。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

1)將內(nèi)存區(qū)域分成兩部分,每次操作其中一個(gè)。

2)當(dāng)進(jìn)行垃圾回收時(shí),將正在使用的內(nèi)存區(qū)域中的存活對(duì)象移動(dòng)到未使用的內(nèi)存區(qū)域。當(dāng)移動(dòng)完對(duì)這部分內(nèi)存區(qū)域一次性清除。

3)周而復(fù)始。

優(yōu)點(diǎn):

  • 在垃圾對(duì)象多的情況下,效率較高
  • 清理后,內(nèi)存無(wú)碎片

缺點(diǎn):

  • 分配的2塊內(nèi)存空間,在同一個(gè)時(shí)刻,只能使用一半,內(nèi)存使用率較低
  • 對(duì)于存活率高的對(duì)象進(jìn)行復(fù)制就會(huì)非常消耗時(shí)間
3.3.3 標(biāo)記整理算法
標(biāo)記壓縮算法是在標(biāo)記清除算法的基礎(chǔ)之上,做了優(yōu)化改進(jìn)的算法。和標(biāo)記清除算法一樣,也是從根節(jié)點(diǎn)開(kāi)始,對(duì)對(duì)象的引用進(jìn)行標(biāo)記,在清理階段,并不是簡(jiǎn)單的直接清理可回收對(duì)象,而是將存活對(duì)象都向內(nèi)存另一端移動(dòng),然后清理邊界以外的垃圾,從而解決了碎片化的問(wèn)題。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

1)標(biāo)記垃圾。

2)需要清除向右邊走,不需要清除的向左邊走。

3)清除邊界以外的垃圾。

優(yōu)缺點(diǎn)同標(biāo)記清除算法,解決了標(biāo)記清除算法的碎片化的問(wèn)題,同時(shí),標(biāo)記壓縮算法多了一步,對(duì)象移動(dòng)內(nèi)存位置的步驟,其效率也有有一定的影響。

與復(fù)制算法對(duì)比:復(fù)制算法標(biāo)記完就復(fù)制,但標(biāo)記整理算法得等把所有存活對(duì)象都標(biāo)記完畢,再進(jìn)行整理
如上三種GC算法則是JVM虛擬機(jī)的基礎(chǔ)GC算法,綜合對(duì)比來(lái)看:

  • 收集速度:復(fù)制算法 > 標(biāo)-清算法 > 標(biāo)-整算法
  • 內(nèi)存整齊度:復(fù)制算法 = 標(biāo)-整算法 > 標(biāo)-清算法
  • 內(nèi)存利用率:標(biāo)-整算法 > 標(biāo)-清算法 > 復(fù)制算法

在GC算法中,速度快的需要用空間來(lái)?yè)Q,空間利用率高且整齊度高的,則需要犧牲時(shí)間來(lái)?yè)Q取,所以相對(duì)而言,在絕大部分算法中,時(shí)間與空間不可兼得。
在上述三種算法中,復(fù)制算法和標(biāo)-整算法都是基于標(biāo)-清算法進(jìn)行優(yōu)化的,所以在現(xiàn)代的高性能JVM中絕大多數(shù)虛擬機(jī)都采用復(fù)制或標(biāo)-整算法進(jìn)行垃圾收集工作。

3.4 分代收集算法

說(shuō)一下JM中的分代回收一、堆的區(qū)域劃分

  1. 堆被分為了兩份:新生代和老年代【1:2】
  2. 對(duì)于新生代,內(nèi)部又被分為了三個(gè)區(qū)域。Eden區(qū),幸存者區(qū)survivor(分成from和to)【8:1:1】

二、對(duì)象回收分代回收策略

  1. 新創(chuàng)建的對(duì)象,都會(huì)先分配到eden區(qū)
  2. 當(dāng)伊甸園內(nèi)存不足,標(biāo)記伊甸園與from(現(xiàn)階段沒(méi)有)的存活對(duì)象
  3. 將存活對(duì)象采用復(fù)制算法復(fù)制到to中,復(fù)制完畢后,伊甸園和 from 內(nèi)存都得到釋放
  4. 經(jīng)過(guò)一段時(shí)間后伊甸園的內(nèi)存又出現(xiàn)不足,標(biāo)記eden區(qū)域to區(qū)存活的對(duì)象,將其復(fù)制到from區(qū)
  5. 當(dāng)幸存區(qū)對(duì)象熬過(guò)幾次回收(最多15次),晉升到老年代(幸存區(qū)內(nèi)存不足或大對(duì)象會(huì)提前晉升)

MinorGC、Mixed GC、FullGC的區(qū)別是什么

  • MinorGC 【young GC】發(fā)生在新生代的垃圾回收,暫停時(shí)間短(STW)
  • Mixed GC新生代+老年代部分區(qū)域的垃圾回收,G1 收集器特有
  • FWGC:新生代+老年代完整垃圾回收,暫
3.4.1 概述

在java8時(shí),堆被分為了兩份:新生代和老年代【1:2】,在java7時(shí),還存在一個(gè)永久代。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

對(duì)于新生代,內(nèi)部又被分為了三個(gè)區(qū)域。Eden區(qū),S0區(qū),S1區(qū)【8:1:1】

當(dāng)對(duì)新生代產(chǎn)生GC:MinorGC【young GC】

當(dāng)對(duì)老年代代產(chǎn)生GC:Major GC

當(dāng)對(duì)新生代和老年代產(chǎn)生FullGC: 新生代 + 老年代完整垃圾回收,暫停時(shí)間長(zhǎng),應(yīng)盡力避免

3.4.2工作機(jī)制

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 新創(chuàng)建的對(duì)象,都會(huì)先分配到eden區(qū)

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 當(dāng)伊甸園內(nèi)存不足,標(biāo)記伊甸園與 from(現(xiàn)階段沒(méi)有)的存活對(duì)象
  • 將存活對(duì)象采用復(fù)制算法復(fù)制到 to 中,復(fù)制完畢后,伊甸園和 from 內(nèi)存都得到釋放

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 經(jīng)過(guò)一段時(shí)間后伊甸園的內(nèi)存又出現(xiàn)不足,標(biāo)記eden區(qū)域to區(qū)存活的對(duì)象,將存活的對(duì)象復(fù)制到from區(qū)

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 當(dāng)幸存區(qū)對(duì)象熬過(guò)幾次回收(最多15次),晉升到老年代(幸存區(qū)內(nèi)存不足或大對(duì)象會(huì)導(dǎo)致提前晉升)

MinorGC、 Mixed GC 、 FullGC的區(qū)別是什么

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • MinorGC【young GC】發(fā)生在新生代的垃圾回收,暫停時(shí)間短(STW)
  • Mixed GC 新生代 + 老年代部分區(qū)域的垃圾回收,G1 收集器特有
  • FullGC: 新生代 + 老年代完整垃圾回收,暫停時(shí)間長(zhǎng)(STW),應(yīng)盡力避免?

名詞解釋?zhuān)?/p>

STW(Stop-The-World):暫停所有應(yīng)用程序線(xiàn)程,等待垃圾回收的完成

3.5 說(shuō)一下 JVM 有哪些垃圾回收器?

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆☆

在jvm中,實(shí)現(xiàn)了多種垃圾收集器,包括:

  • 串行垃圾收集器:Serial GC、Serial Old GC
  • 并行垃圾收集器:Parallel Old GC、ParNew GC
  • CMS (并發(fā))垃圾收集器:CMS GC,作用在老年代
  • G1垃圾回收器
3.5.1 串行垃圾收集器

Serial和Serial Old串行垃圾收集器,是指使用單線(xiàn)程進(jìn)行垃圾回收,堆內(nèi)存較小,適合個(gè)人電腦

  • Serial 作用于新生代,采用復(fù)制算法
  • Serial Old 作用于老年代,采用標(biāo)記-整理算法

垃圾回收時(shí),只有一個(gè)線(xiàn)程在工作,并且java應(yīng)用中的所有線(xiàn)程都要暫停(STW),等待垃圾回收的完成。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3.5.2 并行垃圾收集器

Parallel New和Parallel Old是一個(gè)并行垃圾回收器,JDK8默認(rèn)使用此垃圾回收器

  • Parallel New作用于新生代,采用復(fù)制算法
  • Parallel Old作用于老年代,采用標(biāo)記-整理算法

垃圾回收時(shí),多個(gè)線(xiàn)程在工作,并且java應(yīng)用中的所有線(xiàn)程都要暫停(STW),等待垃圾回收的完成。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3.5.2 CMS(并發(fā))垃圾收集器

CMS全稱(chēng) Concurrent Mark Sweep,是一款并發(fā)的、使用標(biāo)記-清除算法的垃圾回收器,該回收器是針對(duì)老年代垃圾回收的,是一款以獲取最短回收停頓時(shí)間為目標(biāo)的收集器,停頓時(shí)間短,用戶(hù)體驗(yàn)就好。其最大特點(diǎn)是在進(jìn)行垃圾回收時(shí),應(yīng)用仍然能正常運(yùn)行。

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3.6 詳細(xì)聊一下G1垃圾回收器

總結(jié)
詳細(xì)聊一下G1垃圾回收器

  • 應(yīng)用于新生代和老年代,在JDK9之后默認(rèn)使用G1
  • 劃分成多個(gè)區(qū)域,每個(gè)區(qū)域都可以充當(dāng) eden,survivor,old,humongous
    ,其中 humongous 專(zhuān)為大對(duì)象準(zhǔn)備
  • 采用復(fù)制算法
  • 響應(yīng)時(shí)間與吞吐量兼顧
  • 分成三個(gè)階段:新生代回收(stw)、并發(fā)標(biāo)記(重新標(biāo)記stw)、混合收集
  • 如果并發(fā)失?。椿厥账俣融s不上創(chuàng)建新對(duì)象

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆☆

3.6.1 概述
  • 應(yīng)用于新生代和老年代,在JDK9之后默認(rèn)使用G1
  • 劃分成多個(gè)區(qū)域,每個(gè)區(qū)域都可以充當(dāng) eden,survivor,old, humongous,其中 humongous 專(zhuān)為大對(duì)象準(zhǔn)備
  • 采用復(fù)制算法
  • 響應(yīng)時(shí)間與吞吐量兼顧
  • 分成三個(gè)階段:新生代回收、并發(fā)標(biāo)記、混合收集
  • 如果并發(fā)失敗(即回收速度趕不上創(chuàng)建新對(duì)象速度),會(huì)觸發(fā) Full GC

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3.6.2 Young Collection(年輕代垃圾回收)
  • 初始時(shí),所有區(qū)域都處于空閑狀態(tài)
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
  • 創(chuàng)建了一些對(duì)象,挑出一些空閑區(qū)域作為伊甸園區(qū)存儲(chǔ)這些對(duì)象
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
  • 當(dāng)伊甸園需要垃圾回收時(shí),挑出一個(gè)空閑區(qū)域作為幸存區(qū),用復(fù)制算法復(fù)制存活對(duì)象,需要暫停用戶(hù)線(xiàn)程
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
  • 隨著時(shí)間流逝,伊甸園的內(nèi)存又有不足
  • 將伊甸園以及之前幸存區(qū)中的存活對(duì)象,采用復(fù)制算法,復(fù)制到新的幸存區(qū),其中較老對(duì)象晉升至老年代
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
3.6.3 Young Collection + Concurrent Mark (年輕代垃圾回收+并發(fā)標(biāo)記)

當(dāng)老年代占用內(nèi)存超過(guò)閾值(默認(rèn)是45%)后,觸發(fā)并發(fā)標(biāo)記,這時(shí)無(wú)需暫停用戶(hù)線(xiàn)程

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

  • 并發(fā)標(biāo)記之后,會(huì)有重新標(biāo)記階段解決漏標(biāo)問(wèn)題,此時(shí)需要暫停用戶(hù)線(xiàn)程。
  • 這些都完成后就知道了老年代有哪些存活對(duì)象,隨后進(jìn)入混合收集階段。此時(shí)不會(huì)對(duì)所有老年代區(qū)域進(jìn)行回收,而是根據(jù)暫停時(shí)間目標(biāo)優(yōu)先回收價(jià)值高(存活對(duì)象少)的區(qū)域(這也是 Gabage First 名稱(chēng)的由來(lái))。
    JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
3.6.4 Mixed Collection (混合垃圾回收)

復(fù)制完成,內(nèi)存得到釋放。進(jìn)入下一輪的新生代回收、并發(fā)標(biāo)記、混合收集

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

其中H叫做巨型對(duì)象,如果對(duì)象非常大,會(huì)開(kāi)辟一塊連續(xù)的空間存儲(chǔ)巨型對(duì)象

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3.7 強(qiáng)引用、軟引用、弱引用、虛引用的區(qū)別?

強(qiáng)引用、軟引用、弱引用、虛引用的區(qū)別?

  • 強(qiáng)引用:只要所有 GC Roots 能找到,就不會(huì)被回收
  • 軟引用:需要配合SoftReference使用,當(dāng)垃圾多次回收,內(nèi)存依然不夠的時(shí)候會(huì)回收軟引用對(duì)象
  • 弱引用:需要配合WeakReference使用,只要進(jìn)行了垃圾回收,就會(huì)把弱引用對(duì)象回收
  • 虛引用:必須配合引用隊(duì)列使用,被引用對(duì)象回收時(shí),會(huì)將虛引用入隊(duì),由 Reference Handler 線(xiàn)程調(diào)用虛引

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆

3.7.1 強(qiáng)引用

強(qiáng)引用:只有所有 GC Roots 對(duì)象都不通過(guò)【強(qiáng)引用】引用該對(duì)象,該對(duì)象才能被垃圾回收

User user = new User();

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3.7.2 軟引用

軟引用:僅有軟引用引用該對(duì)象時(shí),在垃圾回收后,內(nèi)存仍不足時(shí)會(huì)再次出發(fā)垃圾回收

User user = new User();
SoftReference softReference = new SoftReference(user);

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3.7.3 弱引用

弱引用:僅有弱引用引用該對(duì)象時(shí),在垃圾回收時(shí),無(wú)論內(nèi)存是否充足,都會(huì)回收弱引用對(duì)象

User user = new User();
WeakReference weakReference = new WeakReference(user);

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

延伸話(huà)題:ThreadLocal內(nèi)存泄漏問(wèn)題

ThreadLocal用的就是弱引用,看以下源碼:

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v; //強(qiáng)引用,不會(huì)被回收
     }
}

Entry的key是當(dāng)前ThreadLocal,value值是我們要設(shè)置的數(shù)據(jù)。

WeakReference表示的是弱引用,當(dāng)JVM進(jìn)行GC時(shí),一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間是否足夠,都會(huì)回收它的內(nèi)存。但是value是強(qiáng)引用,它不會(huì)被回收掉。

ThreadLocal使用建議:使用完畢后注意調(diào)用清理方法。

3.7.4 虛引用

虛引用:必須配合引用隊(duì)列使用,被引用對(duì)象回收時(shí),會(huì)將虛引用入隊(duì),由 Reference Handler 線(xiàn)程調(diào)用虛引用相關(guān)方法釋放直接內(nèi)存

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4 JVM實(shí)踐(調(diào)優(yōu))

4.1 JVM 調(diào)優(yōu)的參數(shù)可以在哪里設(shè)置參數(shù)值?

  • 難易程度:☆☆

出現(xiàn)頻率:☆☆☆

4.1.1 tomcat的設(shè)置vm參數(shù)

修改TOMCAT_HOME/bin/catalina.sh文件,如下圖

JAVA_OPTS="-Xms512m -Xmx1024m"

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4.1.2 springboot項(xiàng)目jar文件啟動(dòng)

通常在linux系統(tǒng)下直接加參數(shù)啟動(dòng)springboot項(xiàng)目

nohup java -Xms512m -Xmx1024m -jar xxxx.jar --spring.profiles.active=prod &

nohup : 用于在系統(tǒng)后臺(tái)不掛斷地運(yùn)行命令,退出終端不會(huì)影響程序的運(yùn)行

參數(shù) & :讓命令在后臺(tái)執(zhí)行,終端退出后命令仍舊執(zhí)行。

4.2 用的 JVM 調(diào)優(yōu)的參數(shù)都有哪些?

  • 設(shè)置堆空間大小
  • 虛擬機(jī)棧的設(shè)置
  • 年輕代中Eden區(qū)和兩個(gè)Survivor區(qū)的大小比例
  • 年輕代晉升老年代閾值

難易程度:☆☆☆

出現(xiàn)頻率:☆☆☆☆

對(duì)于JVM調(diào)優(yōu),主要就是調(diào)整年輕代、年老大、元空間的內(nèi)存空間大小及使用的垃圾回收器類(lèi)型。

https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html

1)設(shè)置堆的初始大小和最大大小,為了防止垃圾收集器在初始大小、最大大小之間收縮堆而產(chǎn)生額外的時(shí)間,通常把最大、初始大小設(shè)置為相同的值。

-Xms:設(shè)置堆的初始化大小

-Xmx:設(shè)置堆的最大大小

2) 設(shè)置年輕代中Eden區(qū)和兩個(gè)Survivor區(qū)的大小比例。該值如果不設(shè)置,則默認(rèn)比例為8:1:1。Java官方通過(guò)增大Eden區(qū)的大小,來(lái)減少YGC發(fā)生的次數(shù),但有時(shí)我們發(fā)現(xiàn),雖然次數(shù)減少了,但Eden區(qū)滿(mǎn)的時(shí)候,由于占用的空間較大,導(dǎo)致釋放緩慢,此時(shí)STW的時(shí)間較長(zhǎng),因此需要按照程序情況去調(diào)優(yōu)。

-XXSurvivorRatio=3,表示年輕代中的分配比率:survivor:eden = 2:3

3)年輕代和老年代默認(rèn)比例為1:2??梢酝ㄟ^(guò)調(diào)整二者空間大小比率來(lái)設(shè)置兩者的大小。

-XX:newSize   設(shè)置年輕代的初始大小
-XX:MaxNewSize   設(shè)置年輕代的最大大小,  初始大小和最大大小兩個(gè)值通常相同

4)線(xiàn)程堆棧的設(shè)置每個(gè)線(xiàn)程默認(rèn)會(huì)開(kāi)啟1M的堆棧,用于存放棧幀、調(diào)用參數(shù)、局部變量等,但一般256K就夠用。通常減少每個(gè)線(xiàn)程的堆棧,可以產(chǎn)生更多的線(xiàn)程,但這實(shí)際上還受限于操作系統(tǒng)。

-Xss   對(duì)每個(gè)線(xiàn)程stack大小的調(diào)整,-Xss128k

5)一般來(lái)說(shuō),當(dāng)survivor區(qū)不夠大或者占用量達(dá)到50%,就會(huì)把一些對(duì)象放到老年區(qū)。通過(guò)設(shè)置合理的eden區(qū),survivor區(qū)及使用率,可以將年輕對(duì)象保存在年輕代,從而避免full GC,使用-Xmn設(shè)置年輕代的大小

6)系統(tǒng)CPU持續(xù)飆高的話(huà),首先先排查代碼問(wèn)題,如果代碼沒(méi)問(wèn)題,則咨詢(xún)運(yùn)維或者云服務(wù)器供應(yīng)商,通常服務(wù)器重啟或者服務(wù)器遷移即可解決。

7)對(duì)于占用內(nèi)存比較多的大對(duì)象,一般會(huì)選擇在老年代分配內(nèi)存。如果在年輕代給大對(duì)象分配內(nèi)存,年輕代內(nèi)存不夠了,就要在eden區(qū)移動(dòng)大量對(duì)象到老年代,然后這些移動(dòng)的對(duì)象可能很快消亡,因此導(dǎo)致full GC。通過(guò)設(shè)置參數(shù):-XX:PetenureSizeThreshold=1000000,單位為B,標(biāo)明對(duì)象大小超過(guò)1M時(shí),在老年代(tenured)分配內(nèi)存空間。

8)一般情況下,年輕對(duì)象放在eden區(qū),當(dāng)?shù)谝淮蜧C后,如果對(duì)象還存活,放到survivor區(qū),此后,每GC一次,年齡增加1,當(dāng)對(duì)象的年齡達(dá)到閾值,就被放到tenured老年區(qū)。這個(gè)閾值可以同構(gòu)-XX:MaxTenuringThreshold設(shè)置。如果想讓對(duì)象留在年輕代,可以設(shè)置比較大的閾值。

(1)-XX:+UseParallelGC:年輕代使用并行垃圾回收收集器。這是一個(gè)關(guān)注吞吐量的收集器,可以盡可能的減少垃圾回收時(shí)間。

(2)-XX:+UseParallelOldGC:設(shè)置老年代使用并行垃圾回收收集器。

9)嘗試使用大的內(nèi)存分頁(yè):使用大的內(nèi)存分頁(yè)增加CPU的內(nèi)存尋址能力,從而系統(tǒng)的性能。

-XX:+LargePageSizeInBytes 設(shè)置內(nèi)存頁(yè)的大小

10)使用非占用的垃圾收集器。

-XX:+UseConcMarkSweepGC老年代使用CMS收集器降低停頓。

4.3 說(shuō)一下 JVM 調(diào)優(yōu)的工具?

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆☆

4.3.1 命令工具
4.3.1.1 jps(Java Process Status)

輸出JVM中運(yùn)行的進(jìn)程狀態(tài)信息(現(xiàn)在一般使用jconsole)

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4.3.1.2 jstack

查看java進(jìn)程內(nèi)線(xiàn)程的堆棧信息。

jstack [option] <pid>

java案例

package com.heima.jvm;

public class Application {

    public static void main(String[] args) throws InterruptedException {
        while (true){
            Thread.sleep(1000);
            System.out.println("哈哈哈");
        }
    }
}

使用jstack查看進(jìn)行堆棧運(yùn)行信息

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4.3.1.3 jmap

用于生成堆轉(zhuǎn)存快照

jmap [options] pid 內(nèi)存映像信息

jmap -heap pid 顯示Java堆的信息

jmap -dump:format=b,file=heap.hprof pid

  format=b表示以hprof二進(jìn)制格式轉(zhuǎn)儲(chǔ)Java堆的內(nèi)存

file=用于指定快照dump文件的文件名。

例:顯示了某一個(gè)java運(yùn)行的堆信息

C:\Users\yuhon>jmap -heap 53280
Attaching to process ID 53280, 	please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.321-b07

using thread-local object allocation.
Parallel GC with 8 thread(s)   //并行的垃圾回收器

Heap Configuration:  //堆配置
   MinHeapFreeRatio         = 0   //空閑堆空間的最小百分比
   MaxHeapFreeRatio         = 100  //空閑堆空間的最大百分比
   MaxHeapSize              = 8524922880 (8130.0MB) //堆空間允許的最大值
   NewSize                  = 178257920 (170.0MB) //新生代堆空間的默認(rèn)值
   MaxNewSize               = 2841640960 (2710.0MB) //新生代堆空間允許的最大值
   OldSize                  = 356515840 (340.0MB) //老年代堆空間的默認(rèn)值
   NewRatio                 = 2 //新生代與老年代的堆空間比值,表示新生代:老年代=1:2
   SurvivorRatio            = 8 //兩個(gè)Survivor區(qū)和Eden區(qū)的堆空間比值為8,表示S0:S1:Eden=1:1:8
   MetaspaceSize            = 21807104 (20.796875MB) //元空間的默認(rèn)值
   CompressedClassSpaceSize = 1073741824 (1024.0MB) //壓縮類(lèi)使用空間大小
   MaxMetaspaceSize         = 17592186044415 MB //元空間允許的最大值
   G1HeapRegionSize         = 0 (0.0MB)//在使用 G1 垃圾回收算法時(shí),JVM 會(huì)將 Heap 空間分隔為若干個(gè) Region,該參數(shù)用來(lái)指定每個(gè) Region 空間的大小。

Heap Usage:
PS Young Generation
Eden Space: //Eden使用情況
   capacity = 134217728 (128.0MB)
   used     = 10737496 (10.240074157714844MB)
   free     = 123480232 (117.75992584228516MB)
   8.000057935714722% used
From Space: //Survivor-From 使用情況
   capacity = 22020096 (21.0MB)
   used     = 0 (0.0MB)
   free     = 22020096 (21.0MB)
   0.0% used
To Space: //Survivor-To 使用情況
   capacity = 22020096 (21.0MB)
   used     = 0 (0.0MB)
   free     = 22020096 (21.0MB)
   0.0% used
PS Old Generation  //老年代 使用情況
   capacity = 356515840 (340.0MB)
   used     = 0 (0.0MB)
   free     = 356515840 (340.0MB)
   0.0% used

3185 interned Strings occupying 261264 bytes.
4.3.1.4 jhat

用于分析jmap生成的堆轉(zhuǎn)存快照(一般不推薦使用,而是使用Ecplise Memory Analyzer)

4.3.1.5 jstat

是JVM統(tǒng)計(jì)監(jiān)測(cè)工具??梢杂脕?lái)顯示垃圾回收信息、類(lèi)加載信息、新生代統(tǒng)計(jì)信息等。

常見(jiàn)參數(shù)

①總結(jié)垃圾回收統(tǒng)計(jì)

jstat -gcutil pid

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

字段 含義
S0 幸存1區(qū)當(dāng)前使用比例
S1 幸存2區(qū)當(dāng)前使用比例
E 伊甸園區(qū)使用比例
O 老年代使用比例
M 元數(shù)據(jù)區(qū)使用比例
CCS 壓縮使用比例
YGC 年輕代垃圾回收次數(shù)
YGCT 年輕代垃圾回收消耗時(shí)間
FGC 老年代垃圾回收次數(shù)
FGCT 老年代垃圾回收消耗時(shí)間
GCT 垃圾回收消耗總時(shí)間

②垃圾回收統(tǒng)計(jì)

jstat -gc pid

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4.3.2 可視化工具
4.3.2.1 jconsole

用于對(duì)jvm的內(nèi)存,線(xiàn)程,類(lèi) 的監(jiān)控,是一個(gè)基于 jmx 的 GUI 性能監(jiān)控工具

打開(kāi)方式:java 安裝目錄 bin目錄下 直接啟動(dòng) jconsole.exe 就行

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

可以?xún)?nèi)存、線(xiàn)程、類(lèi)等信息

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記
還可以在線(xiàn)程部分檢查死鎖問(wèn)題

4.3.2.2 VisualVM:故障處理工具

能夠監(jiān)控線(xiàn)程,內(nèi)存情況,查看方法的CPU時(shí)間和內(nèi)存中的對(duì) 象,已被GC的對(duì)象,反向查看分配的堆棧

打開(kāi)方式:java 安裝目錄 bin目錄下 直接啟動(dòng) jvisualvm.exe就行

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

監(jiān)控程序運(yùn)行情況

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

查看運(yùn)行中的dump

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

查看堆中的信息

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4.4 java內(nèi)存泄露的排查思路?

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆☆

原因:

如果線(xiàn)程請(qǐng)求分配的棧容量超過(guò)java虛擬機(jī)棧允許的最大容量的時(shí)候,java虛擬機(jī)將拋出一個(gè)StackOverFlowError異常

如果java虛擬機(jī)??梢詣?dòng)態(tài)拓展,并且擴(kuò)展的動(dòng)作已經(jīng)嘗試過(guò),但是目前無(wú)法申請(qǐng)到足夠的內(nèi)存去完成拓展,或者在建立新線(xiàn)程的時(shí)候沒(méi)有足夠的內(nèi)存去創(chuàng)建對(duì)應(yīng)的虛擬機(jī)棧,那java虛擬機(jī)將會(huì)拋出一個(gè)OutOfMemoryError異常

如果一次加載的類(lèi)太多,元空間內(nèi)存不足,則會(huì)報(bào)OutOfMemoryError: Metaspace

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

1、通過(guò)jmap指定打印他的內(nèi)存快照 dump

有的情況是內(nèi)存溢出之后程序則會(huì)直接中斷,而jmap只能打印在運(yùn)行中的程序,所以建議通過(guò)參數(shù)的方式的生成dump文件,配置如下:

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/app/dumps/ 指定生成后文件的保存目錄

2、通過(guò)工具, VisualVM(Ecplise MAT)去分析 dump文件

VisualVM可以加載離線(xiàn)的dump文件,如下圖

文件–>裝入—>選擇dump文件即可查看堆快照信息

如果是linux系統(tǒng)中的程序,則需要把dump文件下載到本地(windows環(huán)境)下,打開(kāi)VisualVM工具分析。VisualVM目前只支持在windows環(huán)境下運(yùn)行可視化

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

3、通過(guò)查看堆信息的情況,可以大概定位內(nèi)存溢出是哪行代碼出了問(wèn)題

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4、找到對(duì)應(yīng)的代碼,通過(guò)閱讀上下文的情況,進(jìn)行修復(fù)即可

4.5 CPU飆高排查方案與思路?

難易程度:☆☆☆☆

出現(xiàn)頻率:☆☆☆☆

1.使用top命令查看占用cpu的情況

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

2.通過(guò)top命令查看后,可以查看是哪一個(gè)進(jìn)程占用cpu較高,上圖所示的進(jìn)程為:30978

3.查看當(dāng)前線(xiàn)程中的進(jìn)程信息

ps H -eo pid,tid,%cpu | grep 40940

pid 進(jìn)行id

tid 進(jìn)程中的線(xiàn)程id

% cpu使用率

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

4.通過(guò)上圖分析,在進(jìn)程30978中的線(xiàn)程30979占用cpu較高

注意:上述的線(xiàn)程id是一個(gè)十進(jìn)制,我們需要把這個(gè)線(xiàn)程id轉(zhuǎn)換為16進(jìn)制才行,因?yàn)橥ǔT谌罩局姓故镜亩际?6進(jìn)制的線(xiàn)程id名稱(chēng)

轉(zhuǎn)換方式:

在linux中執(zhí)行命令

printf "%x\n" 30979

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

5.可以根據(jù)線(xiàn)程 id 找到有問(wèn)題的線(xiàn)程,進(jìn)一步定位到問(wèn)題代碼的源碼行號(hào)

執(zhí)行命令

jstack 30978   此處是進(jìn)程id

JVM常見(jiàn)問(wèn)題筆記分享,JVM,jvm,筆記

5.面試現(xiàn)場(chǎng)

5.1 JVM組成

面試官:JVM由那些部分組成,運(yùn)行流程是什么?

候選人:

嗯,好的~~

在JVM中共有四大部分,分別是ClassLoader(類(lèi)加載器)、Runtime Data Area(運(yùn)行時(shí)數(shù)據(jù)區(qū),內(nèi)存分區(qū))、Execution Engine(執(zhí)行引擎)、Native Method Library(本地庫(kù)接口)

它們的運(yùn)行流程是:

第一,類(lèi)加載器(ClassLoader)把Java代碼轉(zhuǎn)換為字節(jié)碼

第二,運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)把字節(jié)碼加載到內(nèi)存中,而字節(jié)碼文件只是JVM的一套指令集規(guī)范,并不能直接交給底層系統(tǒng)去執(zhí)行,而是有執(zhí)行引擎運(yùn)行

第三,執(zhí)行引擎(Execution Engine)將字節(jié)碼翻譯為底層系統(tǒng)指令,再交由CPU執(zhí)行去執(zhí)行,此時(shí)需要調(diào)用其他語(yǔ)言的本地庫(kù)接口(Native Method Library)來(lái)實(shí)現(xiàn)整個(gè)程序的功能。

面試官:好的,你能詳細(xì)說(shuō)一下 JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)嗎?

候選人:

嗯,好~

運(yùn)行時(shí)數(shù)據(jù)區(qū)包含了堆、方法區(qū)、棧、本地方法棧、程序計(jì)數(shù)器這幾部分,每個(gè)功能作用不一樣。

  • 堆解決的是對(duì)象實(shí)例存儲(chǔ)的問(wèn)題,垃圾回收器管理的主要區(qū)域。
  • 方法區(qū)可以認(rèn)為是堆的一部分,用于存儲(chǔ)已被虛擬機(jī)加載的信息,常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼。
  • 棧解決的是程序運(yùn)行的問(wèn)題,棧里面存的是棧幀,棧幀里面存的是局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。
  • 本地方法棧與棧功能相同,本地方法棧執(zhí)行的是本地方法,一個(gè)Java調(diào)用非Java代碼的接口。
  • 程序計(jì)數(shù)器(PC寄存器)程序計(jì)數(shù)器中存放的是當(dāng)前線(xiàn)程所執(zhí)行的字節(jié)碼的行數(shù)。JVM工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一個(gè)需要執(zhí)行的字節(jié)碼指令。

面試官:好的,你再詳細(xì)介紹一下程序計(jì)數(shù)器的作用?

候選人:

嗯,是這樣~~

java虛擬機(jī)對(duì)于多線(xiàn)程是通過(guò)線(xiàn)程輪流切換并且分配線(xiàn)程執(zhí)行時(shí)間。在任何的一個(gè)時(shí)間點(diǎn)上,一個(gè)處理器只會(huì)處理執(zhí)行一個(gè)線(xiàn)程,如果當(dāng)前被執(zhí)行的這個(gè)線(xiàn)程它所分配的執(zhí)行時(shí)間用完了【掛起】。處理器會(huì)切換到另外的一個(gè)線(xiàn)程上來(lái)進(jìn)行執(zhí)行。并且這個(gè)線(xiàn)程的執(zhí)行時(shí)間用完了,接著處理器就會(huì)又來(lái)執(zhí)行被掛起的這個(gè)線(xiàn)程。這時(shí)候程序計(jì)數(shù)器就起到了關(guān)鍵作用,程序計(jì)數(shù)器在來(lái)回切換的線(xiàn)程中記錄他上一次執(zhí)行的行號(hào),然后接著繼續(xù)向下執(zhí)行。

面試官:你能給我詳細(xì)的介紹Java堆嗎?

候選人:

好的~

Java中的堆術(shù)語(yǔ)線(xiàn)程共享的區(qū)域。主要用來(lái)保存對(duì)象實(shí)例,數(shù)組等,當(dāng)堆中沒(méi)有內(nèi)存空間可分配給實(shí)例,也無(wú)法再擴(kuò)展時(shí),則拋出OutOfMemoryError異常。

在JAVA8中堆內(nèi)會(huì)存在年輕代、老年代

1)Young區(qū)被劃分為三部分,Eden區(qū)和兩個(gè)大小嚴(yán)格相同的Survivor區(qū),其中,Survivor區(qū)間中,某一時(shí)刻只有其中一個(gè)是被使用的,另外一個(gè)留做垃圾收集時(shí)復(fù)制對(duì)象用。在Eden區(qū)變滿(mǎn)的時(shí)候, GC就會(huì)將存活的對(duì)象移到空閑的Survivor區(qū)間中,根據(jù)JVM的策略,在經(jīng)過(guò)幾次垃圾收集后,任然存活于Survivor的對(duì)象將被移動(dòng)到Tenured區(qū)間。

2)Tenured區(qū)主要保存生命周期長(zhǎng)的對(duì)象,一般是一些老的對(duì)象,當(dāng)一些對(duì)象在Young復(fù)制轉(zhuǎn)移一定的次數(shù)以后,對(duì)象就會(huì)被轉(zhuǎn)移到Tenured區(qū)。

面試官:能不能解釋一下方法區(qū)?

候選人:

好的~

與虛擬機(jī)棧類(lèi)似。本地方法棧是為虛擬機(jī)執(zhí)行本地方法時(shí)提供服務(wù)的。不需要進(jìn)行GC。本地方法一般是由其他語(yǔ)言編寫(xiě)。

面試官:你聽(tīng)過(guò)直接內(nèi)存嗎?

候選人:

嗯~~

它又叫做堆外內(nèi)存,線(xiàn)程共享的區(qū)域,在 Java 8 之前有個(gè)永久代的概念,實(shí)際上指的是 HotSpot 虛擬機(jī)上的永久代,它用永久代實(shí)現(xiàn)了 JVM 規(guī)范定義的方法區(qū)功能,主要存儲(chǔ)類(lèi)的信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后代碼等,這部分由于是在堆中實(shí)現(xiàn)的,受 GC 的管理,不過(guò)由于永久代有 -XX:MaxPermSize 的上限,所以如果大量動(dòng)態(tài)生成類(lèi)(將類(lèi)信息放入永久代),很容易造成 OOM,有人說(shuō)可以把永久代設(shè)置得足夠大,但很難確定一個(gè)合適的大小,受類(lèi)數(shù)量,常量數(shù)量的多少影響很大。

所以在 Java 8 中就把方法區(qū)的實(shí)現(xiàn)移到了本地內(nèi)存中的元空間中,這樣方法區(qū)就不受 JVM 的控制了,也就不會(huì)進(jìn)行 GC,也因此提升了性能。

面試官:什么是虛擬機(jī)棧

候選人:

虛擬機(jī)棧是描述的是方法執(zhí)行時(shí)的內(nèi)存模型,是線(xiàn)程私有的,生命周期與線(xiàn)程相同,每個(gè)方法被執(zhí)行的同時(shí)會(huì)創(chuàng)建棧楨。保存執(zhí)行方法時(shí)的局部變量、動(dòng)態(tài)連接信息、方法返回地址信息等等。方法開(kāi)始執(zhí)行的時(shí)候會(huì)進(jìn)棧,方法執(zhí)行完會(huì)出?!鞠喈?dāng)于清空了數(shù)據(jù)】,所以這塊區(qū)域不需要進(jìn)行 GC。

面試官:能說(shuō)一下堆棧的區(qū)別是什么嗎?

候選人:

嗯,好的,有這幾個(gè)區(qū)別

第一,棧內(nèi)存一般會(huì)用來(lái)存儲(chǔ)局部變量和方法調(diào)用,但堆內(nèi)存是用來(lái)存儲(chǔ)Java對(duì)象和數(shù)組的的。堆會(huì)GC垃圾回收,而棧不會(huì)。

第二、棧內(nèi)存是線(xiàn)程私有的,而堆內(nèi)存是線(xiàn)程共有的。

第三、兩者異常錯(cuò)誤不同,但如果棧內(nèi)存或者堆內(nèi)存不足都會(huì)拋出異常。

??臻g不足:java.lang.StackOverFlowError。

堆空間不足:java.lang.OutOfMemoryError。

5.2 類(lèi)加載器

面試官:什么是類(lèi)加載器,類(lèi)加載器有哪些?

候選人:

嗯,是這樣的

JVM只會(huì)運(yùn)行二進(jìn)制文件,而類(lèi)加載器(ClassLoader)的主要作用就是將字節(jié)碼文件加載到JVM中,從而讓Java程序能夠啟動(dòng)起來(lái)。

常見(jiàn)的類(lèi)加載器有4個(gè)

第一個(gè)是啟動(dòng)類(lèi)加載器(BootStrap ClassLoader):其是由C++編寫(xiě)實(shí)現(xiàn)。用于加載JAVA_HOME/jre/lib目錄下的類(lèi)庫(kù)。

第二個(gè)是擴(kuò)展類(lèi)加載器(ExtClassLoader):該類(lèi)是ClassLoader的子類(lèi),主要加載JAVA_HOME/jre/lib/ext目錄中的類(lèi)庫(kù)。

第三個(gè)是應(yīng)用類(lèi)加載器(AppClassLoader):該類(lèi)是ClassLoader的子類(lèi),主要用于加載classPath下的類(lèi),也就是加載開(kāi)發(fā)者自己編寫(xiě)的Java類(lèi)。

第四個(gè)是自定義類(lèi)加載器:開(kāi)發(fā)者自定義類(lèi)繼承ClassLoader,實(shí)現(xiàn)自定義類(lèi)加載規(guī)則。

面試官:說(shuō)一下類(lèi)裝載的執(zhí)行過(guò)程?

候選人:

嗯,這個(gè)過(guò)程還是挺多的。

類(lèi)從加載到虛擬機(jī)中開(kāi)始,直到卸載為止,它的整個(gè)生命周期包括了:加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用和卸載這7個(gè)階段。其中,驗(yàn)證、準(zhǔn)備和解析這三個(gè)部分統(tǒng)稱(chēng)為連接(linking)

1.加載:查找和導(dǎo)入class文件

2.驗(yàn)證:保證加載類(lèi)的準(zhǔn)確性

3.準(zhǔn)備:為類(lèi)變量分配內(nèi)存并設(shè)置類(lèi)變量初始值

4.解析:把類(lèi)中的符號(hào)引用轉(zhuǎn)換為直接引用

5.初始化:對(duì)類(lèi)的靜態(tài)變量,靜態(tài)代碼塊執(zhí)行初始化操作

6.使用:JVM 開(kāi)始從入口方法開(kāi)始執(zhí)行用戶(hù)的程序代碼

7.卸載:當(dāng)用戶(hù)程序代碼執(zhí)行完畢后,JVM 便開(kāi)始銷(xiāo)毀創(chuàng)建的 Class 對(duì)象,最后負(fù)責(zé)運(yùn)行的 JVM 也退出內(nèi)存

面試官:什么是雙親委派模型?

候選人:

嗯,它是是這樣的。

如果一個(gè)類(lèi)加載器收到了類(lèi)加載的請(qǐng)求,它首先不會(huì)自己嘗試加載這個(gè)類(lèi),而是把這請(qǐng)求委派給父類(lèi)加載器去完成,每一個(gè)層次的類(lèi)加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳說(shuō)到頂層的啟動(dòng)類(lèi)加載器中,只有當(dāng)父類(lèi)加載器返回自己無(wú)法完成這個(gè)加載請(qǐng)求(它的搜索返回中沒(méi)有找到所需的類(lèi))時(shí),子類(lèi)加載器才會(huì)嘗試自己去加載

面試官:JVM為什么采用雙親委派機(jī)制

候選人:

主要有兩個(gè)原因。

第一、通過(guò)雙親委派機(jī)制可以避免某一個(gè)類(lèi)被重復(fù)加載,當(dāng)父類(lèi)已經(jīng)加載后則無(wú)需重復(fù)加載,保證唯一性。

第二、為了安全,保證類(lèi)庫(kù)API不會(huì)被修改

5.3 垃圾回收

面試官:簡(jiǎn)述Java垃圾回收機(jī)制?(GC是什么?為什么要GC)

候選人:

嗯,是這樣~~

為了讓程序員更專(zhuān)注于代碼的實(shí)現(xiàn),而不用過(guò)多的考慮內(nèi)存釋放的問(wèn)題,所以,在Java語(yǔ)言中,有了自動(dòng)的垃圾回收機(jī)制,也就是我們熟悉的GC(Garbage Collection)。

有了垃圾回收機(jī)制后,程序員只需要關(guān)心內(nèi)存的申請(qǐng)即可,內(nèi)存的釋放由系統(tǒng)自動(dòng)識(shí)別完成。

在進(jìn)行垃圾回收時(shí),不同的對(duì)象引用類(lèi)型,GC會(huì)采用不同的回收時(shí)機(jī)

面試官:強(qiáng)引用、軟引用、弱引用、虛引用的區(qū)別?

候選人:

嗯嗯~

強(qiáng)引用最為普通的引用方式,表示一個(gè)對(duì)象處于有用且必須的狀態(tài),如果一個(gè)對(duì)象具有強(qiáng)引用,則GC并不會(huì)回收它。即便堆中內(nèi)存不足了,寧可出現(xiàn)OOM,也不會(huì)對(duì)其進(jìn)行回收

軟引用表示一個(gè)對(duì)象處于有用且非必須狀態(tài),如果一個(gè)對(duì)象處于軟引用,在內(nèi)存空間足夠的情況下,GC機(jī)制并不會(huì)回收它,而在內(nèi)存空間不足時(shí),則會(huì)在OOM異常出現(xiàn)之間對(duì)其進(jìn)行回收。但值得注意的是,因?yàn)镚C線(xiàn)程優(yōu)先級(jí)較低,軟引用并不會(huì)立即被回收。

弱引用表示一個(gè)對(duì)象處于可能有用且非必須的狀態(tài)。在GC線(xiàn)程掃描內(nèi)存區(qū)域時(shí),一旦發(fā)現(xiàn)弱引用,就會(huì)回收到弱引用相關(guān)聯(lián)的對(duì)象。對(duì)于弱引用的回收,無(wú)關(guān)內(nèi)存區(qū)域是否足夠,一旦發(fā)現(xiàn)則會(huì)被回收。同樣的,因?yàn)镚C線(xiàn)程優(yōu)先級(jí)較低,所以弱引用也并不是會(huì)被立刻回收。

虛引用表示一個(gè)對(duì)象處于無(wú)用的狀態(tài)。在任何時(shí)候都有可能被垃圾回收。虛引用的使用必須和引用隊(duì)列Reference Queue聯(lián)合使用

面試官:對(duì)象什么時(shí)候可以被垃圾器回收

候選人:

思考一會(huì)~~

如果一個(gè)或多個(gè)對(duì)象沒(méi)有任何的引用指向它了,那么這個(gè)對(duì)象現(xiàn)在就是垃圾,如果定位了垃圾,則有可能會(huì)被垃圾回收器回收。

如果要定位什么是垃圾,有兩種方式來(lái)確定,第一個(gè)是引用計(jì)數(shù)法,第二個(gè)是可達(dá)性分析算法

通常都使用可達(dá)性分析算法來(lái)確定是不是垃圾

面試官: JVM 垃圾回收算法有哪些?

候選人:

我記得一共有四種,分別是標(biāo)記清除算法、復(fù)制算法、標(biāo)記整理算法、分代回收

面試官: 你能詳細(xì)聊一下分代回收嗎?

候選人:

關(guān)于分代回收是這樣的

在java8時(shí),堆被分為了兩份:新生代和老年代,它們默認(rèn)空間占用比例是1:2

對(duì)于新生代,內(nèi)部又被分為了三個(gè)區(qū)域。Eden區(qū),S0區(qū),S1區(qū)默認(rèn)空間占用比例是8:1:1

具體的工作機(jī)制是有些情況:

1)當(dāng)創(chuàng)建一個(gè)對(duì)象的時(shí)候,那么這個(gè)對(duì)象會(huì)被分配在新生代的Eden區(qū)。當(dāng)Eden區(qū)要滿(mǎn)了時(shí)候,觸發(fā)YoungGC。

2)當(dāng)進(jìn)行YoungGC后,此時(shí)在Eden區(qū)存活的對(duì)象被移動(dòng)到S0區(qū),并且當(dāng)前對(duì)象的年齡會(huì)加1,清空Eden區(qū)。

3)當(dāng)再一次觸發(fā)YoungGC的時(shí)候,會(huì)把Eden區(qū)中存活下來(lái)的對(duì)象和S0中的對(duì)象,移動(dòng)到S1區(qū)中,這些對(duì)象的年齡會(huì)加1,清空Eden區(qū)和S0區(qū)。

4)當(dāng)再一次觸發(fā)YoungGC的時(shí)候,會(huì)把Eden區(qū)中存活下來(lái)的對(duì)象和S1中的對(duì)象,移動(dòng)到S0區(qū)中,這些對(duì)象的年齡會(huì)加1,清空Eden區(qū)和S1區(qū)。

5)對(duì)象的年齡達(dá)到了某一個(gè)限定的值(默認(rèn)15歲 ),那么這個(gè)對(duì)象就會(huì)進(jìn)入到老年代中。

當(dāng)然也有特殊情況,如果進(jìn)入Eden區(qū)的是一個(gè)大對(duì)象,在觸發(fā)YoungGC的時(shí)候,會(huì)直接存放到老年代

當(dāng)老年代滿(mǎn)了之后,觸發(fā)FullGCFullGC同時(shí)回收新生代和老年代,當(dāng)前只會(huì)存在一個(gè)FullGC的線(xiàn)程進(jìn)行執(zhí)行,其他的線(xiàn)程全部會(huì)被掛起。 我們?cè)诔绦蛑幸M量避免FullGC的出現(xiàn)。

面試官:講一下新生代、老年代、永久代的區(qū)別?

候選人:

嗯!是這樣的,簡(jiǎn)單說(shuō)就是

新生代主要用來(lái)存放新生的對(duì)象。

老年代主要存放應(yīng)用中生命周期長(zhǎng)的內(nèi)存對(duì)象。

永久代指的是永久保存區(qū)域。主要存放Class和Meta(元數(shù)據(jù))的信息。在Java8中,永久代已經(jīng)被移除,取而代之的是一個(gè)稱(chēng)之為“元數(shù)據(jù)區(qū)”(元空間)的區(qū)域。元空間和永久代類(lèi)似,不過(guò)元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存。因此,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存的限制。

面試官:說(shuō)一下 JVM 有哪些垃圾回收器?

候選人:

在jvm中,實(shí)現(xiàn)了多種垃圾收集器,包括:串行垃圾收集器、并行垃圾收集器(JDK8默認(rèn))、CMS(并發(fā))垃圾收集器、G1垃圾收集器(JDK9默認(rèn))

面試官:Minor GC、Major GC、Full GC是什么

候選人:

嗯,其實(shí)它們指的是不同代之間的垃圾回收

Minor GC 發(fā)生在新生代的垃圾回收,暫停時(shí)間短

Major GC 老年代區(qū)域的垃圾回收,老年代空間不足時(shí),會(huì)先嘗試觸發(fā)Minor GC。Minor GC之后空間還不足,則會(huì)觸發(fā)Major GC,Major GC速度比較慢,暫停時(shí)間長(zhǎng)

Full GC 新生代 + 老年代完整垃圾回收,暫停時(shí)間長(zhǎng),應(yīng)盡力避免

5.4 JVM實(shí)踐(調(diào)優(yōu))

面試官:JVM 調(diào)優(yōu)的參數(shù)可以在哪里設(shè)置參數(shù)值?

候選人:

我們當(dāng)時(shí)的項(xiàng)目是springboot項(xiàng)目,可以在項(xiàng)目啟動(dòng)的時(shí)候,java -jar中加入?yún)?shù)就行了

面試官:用的 JVM 調(diào)優(yōu)的參數(shù)都有哪些?

候選人:

嗯,這些參數(shù)是比較多的

我記得當(dāng)時(shí)我們?cè)O(shè)置過(guò)堆的大小,像-Xms和-Xmx

還有就是可以設(shè)置年輕代中Eden區(qū)和兩個(gè)Survivor區(qū)的大小比例

還有就是可以設(shè)置使用哪種垃圾回收器等等。具體的指令還真記不太清楚。

面試官:嗯,好的,你們平時(shí)調(diào)試 JVM都用了哪些工具呢?

候選人:

嗯,我們一般都是使用jdk自帶的一些工具,比如

jps 輸出JVM中運(yùn)行的進(jìn)程狀態(tài)信息

jstack查看java進(jìn)程內(nèi)線(xiàn)程的堆棧信息。

jmap 用于生成堆轉(zhuǎn)存快照

jstat用于JVM統(tǒng)計(jì)監(jiān)測(cè)工具

還有一些可視化工具,像jconsole和VisualVM等

面試官:假如項(xiàng)目中產(chǎn)生了java內(nèi)存泄露,你說(shuō)一下你的排查思路?

候選人:

嗯,這個(gè)我在之前項(xiàng)目排查過(guò)

第一呢可以通過(guò)jmap指定打印他的內(nèi)存快照 dump文件,不過(guò)有的情況打印不了,我們會(huì)設(shè)置vm參數(shù)讓程序自動(dòng)生成dump文件

第二,可以通過(guò)工具去分析 dump文件,jdk自帶的VisualVM就可以分析

第三,通過(guò)查看堆信息的情況,可以大概定位內(nèi)存溢出是哪行代碼出了問(wèn)題

第四,找到對(duì)應(yīng)的代碼,通過(guò)閱讀上下文的情況,進(jìn)行修復(fù)即可

面試官:好的,那現(xiàn)在再來(lái)說(shuō)一種情況,就是說(shuō)服務(wù)器CPU持續(xù)飆高,你的排查方案與思路?

候選人:

嗯,我思考一下~~

可以這么做~~

第一可以使用使用top命令查看占用cpu的情況

第二通過(guò)top命令查看后,可以查看是哪一個(gè)進(jìn)程占用cpu較高,記錄這個(gè)進(jìn)程id

第三可以通過(guò)ps 查看當(dāng)前進(jìn)程中的線(xiàn)程信息,看看哪個(gè)線(xiàn)程的cpu占用較高

第四可以jstack命令打印進(jìn)行的id,找到這個(gè)線(xiàn)程,就可以進(jìn)一步定位問(wèn)題代碼的行號(hào)

學(xué)習(xí)視頻:【新版Java面試專(zhuān)題視頻教程,java八股文面試全套真題+深度詳解(含大廠高頻面試真題)】 https://www.bilibili.com/video/BV1yT411H7YK/?p=134&share_source=copy_web&vd_source=fcae3ca58a4c2446a58b5aaacbaa4bbe文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-826936.html

到了這里,關(guān)于JVM常見(jiàn)問(wèn)題筆記分享的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【筆記】Android MTU 知識(shí)及常見(jiàn)問(wèn)題

    運(yùn)營(yíng)商對(duì)MTU配置有需求。比如針對(duì)不同類(lèi)型的APN或者是注冊(cè)網(wǎng)絡(luò)環(huán)境的不同存在需求差異。 不配置時(shí),默認(rèn)MTU為1500(Default MTU size 1500 bytes) 測(cè)試命令:ping -s frameworks/opt/telephony 客制化MTU,最終在packages/modules/Connectivity 模塊會(huì)調(diào)用MTU配置 LinkProperties.java - OpenGrok cross reference

    2024年02月19日
    瀏覽(21)
  • Ftp無(wú)法連接到服務(wù)器怎么辦?常見(jiàn)的ftp錯(cuò)誤問(wèn)題及解決辦法分享

    1、無(wú)法上傳網(wǎng)頁(yè),提示“無(wú)法連接服務(wù)器”錯(cuò)誤; 原因: FTP客戶(hù)端程序設(shè)置問(wèn)題,客戶(hù)上網(wǎng)線(xiàn)路問(wèn)題,ftp服務(wù)器端問(wèn)題。 解決方法: 使用CUTPFTP軟件來(lái)上傳客戶(hù)的網(wǎng)頁(yè),在“FTP主機(jī)地址處”最好填寫(xiě)IP地址。 2、FTP時(shí)已經(jīng)通過(guò)身份驗(yàn)證,但總列不出目錄; 原因: 上傳軟件的

    2024年02月17日
    瀏覽(22)
  • ROS學(xué)習(xí)筆記(實(shí)踐三)--常見(jiàn)相機(jī)問(wèn)題整理

    ROS學(xué)習(xí)筆記(實(shí)踐三)--常見(jiàn)相機(jī)問(wèn)題整理

    安裝ros功能包 啟動(dòng)后彈出如下窗口: 需要修相機(jī)驅(qū)動(dòng)時(shí),可以使用源碼安裝,源碼地址: https://github.com/ros-drivers/usb_cam

    2024年02月15日
    瀏覽(22)
  • 「C#」異步編程玩法筆記-WinForm中的常見(jiàn)問(wèn)題

    「C#」異步編程玩法筆記-WinForm中的常見(jiàn)問(wèn)題

    目錄 1、異步更新界面 1.1、問(wèn)題 1.2、解決問(wèn)題 1.3、AsyncOperationManager和AsyncOperation 1.4、Invoke、BeginInvoke、EndInvoke及InvokeRequired Invoke InvokeRequired BeginInvoke EndInvoke 2、死鎖 2.1、問(wèn)題 2.2、 解決方法 2.2.1、不要await 2.2.2、用await代替Wait()/Result 2.2.3、使用新的異步方法中轉(zhuǎn) 2.2.4、Config

    2024年02月01日
    瀏覽(29)
  • AI聲音克隆模型常見(jiàn)問(wèn)題匯總筆記(附解決方法,可評(píng)論區(qū)留言問(wèn)題技術(shù)交流

    AI聲音克隆模型常見(jiàn)問(wèn)題匯總筆記(附解決方法,可評(píng)論區(qū)留言問(wèn)題技術(shù)交流

    聲明: 源碼非原創(chuàng),轉(zhuǎn)載自小破站UP主Jack-Cui,文章部分內(nèi)容來(lái)源網(wǎng)路,本文只用于技術(shù)分享,模型訓(xùn)練與語(yǔ)音輸出已測(cè)試成功。 硬件配置工具及運(yùn)行環(huán)境 名詞解釋?zhuān)?batch_size :計(jì)算效率和內(nèi)存容量之間的平衡參數(shù)。若為高性能GPU,可以設(shè)置更大的batch_size值 epochs :所有樣本

    2024年02月01日
    瀏覽(22)
  • Flink|《Flink 官方文檔 - 部署 - 內(nèi)存配置 - 調(diào)優(yōu)指南 & 常見(jiàn)問(wèn)題》學(xué)習(xí)筆記

    學(xué)習(xí)文檔: 《Flink 官方文檔 - 部署 - 內(nèi)存配置 - 調(diào)優(yōu)指南》 《Flink 官方文檔 - 部署 - 內(nèi)存配置 - 常見(jiàn)問(wèn)題》 學(xué)習(xí)筆記如下: 獨(dú)立部署模式(Standalone Deployment)下的內(nèi)存配置 通常無(wú)需配置進(jìn)程總內(nèi)存,因?yàn)椴还苁?Flink 還是部署環(huán)境都不會(huì)對(duì) JVM 開(kāi)銷(xiāo)進(jìn)行限制,它只與機(jī)器的

    2024年02月19日
    瀏覽(24)
  • Spacedesk的安裝使用以及常見(jiàn)問(wèn)題解決(實(shí)現(xiàn)筆記本屏幕,手機(jī)屏,平板屏互相連接)

    提示:Spacedesk的安裝使用(實(shí)現(xiàn)筆記本屏幕,手機(jī)屏,平板屏互相連接) 提示:Spacedesk的安裝使用(實(shí)現(xiàn)筆記本屏幕,手機(jī)屏,平板屏互相連接) 提示:以下是本篇文章正文內(nèi)容,下面案例可供參考 實(shí)現(xiàn)電腦分屏功能。 主機(jī):必須下載軟件spacedesk server (https://spacedesk.net/)

    2024年02月08日
    瀏覽(82)
  • JVM調(diào)優(yōu)筆記(一)--Nacos GC引發(fā)的服務(wù)批量下線(xiàn)問(wèn)題

    線(xiàn)上批量發(fā)服務(wù)下線(xiàn)的告警郵件,偶發(fā)nacos連接超時(shí)。采用了spring boot admin(以下稱(chēng)sba)進(jìn)行服務(wù)監(jiān)控。 因?yàn)閟ba服務(wù)是基于nacos對(duì)其它服務(wù)進(jìn)行監(jiān)控,所以遇到這個(gè)問(wèn)題,第一懷疑對(duì)象是nacos發(fā)生問(wèn)題,但不清楚具體是什么問(wèn)題。由于服務(wù)過(guò)一段事件會(huì)恢復(fù),所以nacos肯定是沒(méi)

    2023年04月23日
    瀏覽(26)
  • openGauss學(xué)習(xí)筆記-192 openGauss 數(shù)據(jù)庫(kù)運(yùn)維-常見(jiàn)故障定位案例-XFS文件系統(tǒng)問(wèn)題

    openGauss學(xué)習(xí)筆記-192 openGauss 數(shù)據(jù)庫(kù)運(yùn)維-常見(jiàn)故障定位案例-XFS文件系統(tǒng)問(wèn)題

    192.1 在XFS文件系統(tǒng)中,使用du命令查詢(xún)數(shù)據(jù)文件大小大于文件實(shí)際大小 192.1.1 問(wèn)題現(xiàn)象 在數(shù)據(jù)庫(kù)使用過(guò)程中,通過(guò)如下du命令查詢(xún)數(shù)據(jù)文件大小,查詢(xún)結(jié)果大于文件實(shí)際的大小。 192.1.2 原因分析 XFS文件系統(tǒng)有預(yù)分配機(jī)制,預(yù)分配的大小由參數(shù)allocsize確定。du命令顯示的文件大

    2024年02月02日
    瀏覽(23)
  • openGauss學(xué)習(xí)筆記-196 openGauss 數(shù)據(jù)庫(kù)運(yùn)維-常見(jiàn)故障定位案例-強(qiáng)制結(jié)束指定的問(wèn)題會(huì)話(huà)

    openGauss學(xué)習(xí)筆記-196 openGauss 數(shù)據(jù)庫(kù)運(yùn)維-常見(jiàn)故障定位案例-強(qiáng)制結(jié)束指定的問(wèn)題會(huì)話(huà)

    196.1 強(qiáng)制結(jié)束指定的問(wèn)題會(huì)話(huà) 196.1.1 問(wèn)題現(xiàn)象 有些情況下,為了使系統(tǒng)繼續(xù)提供服務(wù),管理員需要強(qiáng)制結(jié)束有問(wèn)題的會(huì)話(huà)。 196.1.2 處理辦法 以操作系統(tǒng)用戶(hù)omm登錄主機(jī)。 使用如下命令連接數(shù)據(jù)庫(kù)。 postgres為需要連接的數(shù)據(jù)庫(kù)名稱(chēng),8000為端口號(hào)。 從當(dāng)前活動(dòng)會(huì)話(huà)視圖查找問(wèn)

    2024年01月18日
    瀏覽(23)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包