java虛擬機(上篇)內存模型

目錄

 

jvm與jdk、jre關係圖

JVM的生命週期

JVM內存結構

虛擬機運行時內存空間分配

程序計數器

本地方法棧

元空間(方法區)

常量池詳解

(GC)垃圾回收器

垃圾收集器分類

解析器和編譯器


jvm與jdk、jre關係圖

 

JDK:Java Development ToolKit(Java開發工具包)。JDK是整個JAVA的核心,包括了Java運行環境(Java Runtime Envirnment),一堆Java工具(javac/java/jdb等)和Java基礎的類庫(即Java API 包括rt.jar

JRE:java  Runtime  Enviromental(java運行時環境),包括JVM和JAVA核心類庫和支持文件。

JVM:Java Virtual Mechinal(JAVA虛擬機)。JVM是JRE的一部分,它是一個虛構出來的計算機,JVM有自己完善的硬件架構,如處理器、堆棧、寄存器等,還具有相應的指令系統。JVM 的主要工作是解釋自己的指令集(即字節碼)並映射到本地的 CPU 的指令集或 OS 的系統調用,Java語言是跨平臺運行的。

總結:jdk=jre+工具類(javac等)

   jre=jvm+核心類庫

JVM的生命週期

  • JVM實例的誕生

當啓動一個Java程序時,一個JVM實例就產生了,任何一個擁有public static void main(String[] args)函數的class都可以作爲

JVM實例運行的起點

  • JVM實例的運行

 main()作爲該程序初始線程的起點,任何其他線程均由該線程啓動。JVM內部有兩種線程:守護線程和非守護線程,

main()屬於非守護線程,守護線程通常由JVM自己使用,java程序也可以標明自己創建的線程是守護線程。   

  • JVM實例的消亡

 當程序中的所有“非守護線程” 都終止時,JVM才退出;若安全管理器允許,程序也可以使用java.lang.Runtime類

或者java.lang.System.exit()來退出。

 

舉例

static class JvmLifeTest extends TimerTask{

    /**
     * The action to be performed by this timer task.
     */
    @Override
    public void run() {
        System.out.println("用戶線程---非守護線程");
    }
}

public static void main(String[] args) throws InterruptedException {
    JvmLifeTest lifeTest = new JvmLifeTest();
    //定時器
    Timer timer = new Timer();
    //非守護線程
    timer.schedule(lifeTest,new Date());
    System.out.println("main  結束啦");
}

 

JVM內存結構

  • 類加載子系統

啓動(Bootstrap)類加載器

引導類加載器是用 本地代碼實現的類加載器,它負責將 <JAVA_HOME>/lib下面的核心類庫 或 -Xbootclasspath

選項指定的jar包等 虛擬機識別的類庫 加載到內存中。由於引導類加載器涉及到虛擬機本地實現細節,

開發者無法直接獲取到啓動類加載器的引用,所以 不允許直接通過引用進行操作。

擴展(Extension)類加載器
       擴展類加載器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實現的,

它負責將 <JAVA_HOME >/lib/ext或者由系統變量-Djava.ext.dir指定位置中的類庫 加載到內存中。

開發者可以直接使用標準擴展類加載器。

系統(System、Application)類加載器
       系統類加載器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的,

它負責將 用戶類路徑(java -classpath或-Djava.class.path變量所指的目錄,即當前類所在路徑及其引用的第三方類庫的路徑)

下的類庫 加載到內存中。開發者可以直接使用系統類加載器。

     

代碼展示:

public static void main(String[] args) {
    //bootstrap classloader
    URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
    for(URL url : urLs){
        System.out.println(url);
    }
    System.out.println("----------------華麗的分割線1--------------");
    //ext classloader
    URLClassLoader extClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader().getParent();
    urLs = extClassLoader.getURLs();
    for(URL url : urLs){
        System.out.println(url);
    }
    System.out.println("----------------華麗的分割線2--------------");
    //application classloader
    URLClassLoader appClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
    urLs = appClassLoader.getURLs();
    for(URL url : urLs){
        System.out.println(url);
    }
}

結果:

file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/classes
----------------華麗的分割線--------------
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunec.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/nashorn.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/cldrdata.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jfxrt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/dnsns.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/localedata.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jaccess.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/zipfs.jar
file:/System/Library/Java/Extensions/MRJToolkit.jar
----------------華麗的分割線--------------
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/deploy.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/cldrdata.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/dnsns.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jaccess.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jfxrt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/localedata.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/nashorn.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunec.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/zipfs.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/javaws.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfxswt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/management-agent.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/plugin.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/ant-javafx.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/dt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/javafx-mx.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/jconsole.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/packager.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/sa-jdi.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/tools.jar
file:/Users/xiahui/study/project/gitee-activity/target/classes/
file:/Users/xiahui/applications/mavenLib-study/com/baomidou/mybatis-plus/3.0.3/mybatis-plus-3.0.3.jar
file:/Users/xiahui/applications/mavenLib-study/com/baomidou/mybatis-plus-extension/3.0.3/mybatis-plus-extension-3.0.3.jar
file:/Users/xiahui/applications/mavenLib-study/com/baomidou/mybatis-plus-core/3.0.3/mybatis-plus-core-3.0.3.jar
file:/Users/xiahui/applications/mavenLib-study/com/baomidou/mybatis-plus-annotation/3.0.3/mybatis-plus-annotation-3.0.3.jar
file:/Users/xiahui/applications/mavenLib-study/com/github/jsqlparser/jsqlparser/1.2/jsqlparser-1.2.jar
file:/Users/xiahui/applications/mavenLib-study/org/mybatis/mybatis-spring/1.3.2/mybatis-spring-1.3.2.jar
file:/Users/xiahui/applications/mavenLib-study/com/baomidou/mybatis-plus-generator/3.0.3/mybatis-plus-generator-3.0.3.jar
file:/Users/xiahui/applications/mavenLib-study/org/activiti/activiti-engine/5.15.1/activiti-engine-5.15.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/apache/commons/commons-email/1.2/commons-email-1.2.jar
file:/Users/xiahui/applications/mavenLib-study/javax/mail/mail/1.4.1/mail-1.4.1.jar
file:/Users/xiahui/applications/mavenLib-study/javax/activation/activation/1.1/activation-1.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/apache/commons/commons-lang3/3.1/commons-lang3-3.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/mybatis/mybatis/3.2.5/mybatis-3.2.5.jar
file:/Users/xiahui/applications/mavenLib-study/joda-time/joda-time/2.1/joda-time-2.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/slf4j/jcl-over-slf4j/1.7.6/jcl-over-slf4j-1.7.6.jar
file:/Users/xiahui/applications/mavenLib-study/org/activiti/activiti-spring/5.15.1/activiti-spring-5.15.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/activiti/activiti-bpmn-model/5.15.1/activiti-bpmn-model-5.15.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/codehaus/jackson/jackson-core-asl/1.9.9/jackson-core-asl-1.9.9.jar
file:/Users/xiahui/applications/mavenLib-study/org/activiti/activiti-bpmn-layout/5.15.1/activiti-bpmn-layout-5.15.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/tinyjee/jgraphx/jgraphx/1.10.4.1/jgraphx-1.10.4.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/activiti/activiti-bpmn-converter/5.15.1/activiti-bpmn-converter-5.15.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/activiti/activiti-process-validation/5.15.1/activiti-process-validation-5.15.1.jar
file:/Users/xiahui/applications/mavenLib-study/mysql/mysql-connector-java/5.1.29/mysql-connector-java-5.1.29.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-core/3.2.2.RELEASE/spring-core-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-beans/3.2.2.RELEASE/spring-beans-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-test/3.2.2.RELEASE/spring-test-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/commons-dbcp/commons-dbcp/1.4/commons-dbcp-1.4.jar
file:/Users/xiahui/applications/mavenLib-study/commons-pool/commons-pool/1.5.4/commons-pool-1.5.4.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-context/3.2.2.RELEASE/spring-context-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-expression/3.2.2.RELEASE/spring-expression-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-context-support/3.2.2.RELEASE/spring-context-support-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-tx/3.2.2.RELEASE/spring-tx-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/aopalliance/aopalliance/1.0/aopalliance-1.0.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-orm/3.2.2.RELEASE/spring-orm-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-aop/3.2.2.RELEASE/spring-aop-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-aspects/3.2.2.RELEASE/spring-aspects-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/aspectj/aspectjweaver/1.7.2/aspectjweaver-1.7.2.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-jdbc/3.2.2.RELEASE/spring-jdbc-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-web/3.2.2.RELEASE/spring-web-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/org/springframework/spring-webmvc/3.2.2.RELEASE/spring-webmvc-3.2.2.RELEASE.jar
file:/Users/xiahui/applications/mavenLib-study/junit/junit/4.11/junit-4.11.jar
file:/Users/xiahui/applications/mavenLib-study/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
file:/Users/xiahui/applications/mavenLib-study/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6.jar
file:/Applications/IntelliJ%20IDEA.app/Contents/lib/idea_rt.jar

虛擬機運行時內存空間分配

圖1

 

public class JvmTest {

    protected int hi(){
        int a = 1;
        int b = 2;
        int c = (a+b)*10;
        return c;
    }

    public static void main(String[] args) {
        JvmTest test = new JvmTest();
        int hi = test.hi();
        System.out.println(hi);
    }

}

 

編碼java類爲字節碼

圖2

 

看不懂,我們通過javap -v反彙編(JvmTest.txt)

Classfile /Users/xiahui/study/project/gitee-activity/src/main/java/gitee/activity/jvm/JvmTest.class
  Last modified 2020年5月6日; size 513 bytes
  MD5 checksum 341dec80192863ac2dec469d26d36eb7
  Compiled from "JvmTest.java"
public class gitee.activity.jvm.JvmTest
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2                          // gitee/activity/jvm/JvmTest
  super_class: #7                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 3, attributes: 1
Constant pool:
   #1 = Methodref          #7.#18         // java/lang/Object."<init>":()V
   #2 = Class              #19            // gitee/activity/jvm/JvmTest
   #3 = Methodref          #2.#18         // gitee/activity/jvm/JvmTest."<init>":()V
   #4 = Methodref          #2.#20         // gitee/activity/jvm/JvmTest.hi:()I
   #5 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Methodref          #23.#24        // java/io/PrintStream.println:(I)V
   #7 = Class              #25            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               hi
  #13 = Utf8               ()I
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               SourceFile
  #17 = Utf8               JvmTest.java
  #18 = NameAndType        #8:#9          // "<init>":()V
  #19 = Utf8               gitee/activity/jvm/JvmTest
  #20 = NameAndType        #12:#13        // hi:()I
  #21 = Class              #26            // java/lang/System
  #22 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #23 = Class              #29            // java/io/PrintStream
  #24 = NameAndType        #30:#31        // println:(I)V
  #25 = Utf8               java/lang/Object
  #26 = Utf8               java/lang/System
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Utf8               java/io/PrintStream
  #30 = Utf8               println
  #31 = Utf8               (I)V
{
  public gitee.activity.jvm.JvmTest();
    descriptor: ()V
    flags: (0x0001) 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 10: 0

  protected int hi();
    descriptor: ()I
    flags: (0x0004) ACC_PROTECTED
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: bipush        10
         9: imul
        10: istore_3
        11: iload_3
        12: ireturn
      LineNumberTable:
        line 13: 0
        line 14: 2
        line 15: 4
        line 16: 11

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class gitee/activity/jvm/JvmTest
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method hi:()I
        12: istore_2
        13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: iload_2
        17: invokevirtual #6                  // Method java/io/PrintStream.println:(I)V
        20: return
      LineNumberTable:
        line 20: 0
        line 21: 8
        line 22: 13
        line 23: 20
}
SourceFile: "JvmTest.java"

 

簡單解釋下hi()方法的執行過程:

操作數棧(先進後出)

從序號0開始:

0:iconst_1 : 將int類型的常量1壓入棧

1:istore_1:將int類型的變量1存入局部變量表

2:iconst_2:將int類型的常量2壓入棧

3:istore_2:將int類型的變量2存入局部變量表

4:iload_1:將局部變量表中的1壓入操作數棧中

5:iload_2:將局部變量表中的2壓入到操作數棧

6:iadd:執行int類型的加法運算(在運行的時候將操作數棧中最接近棧頂的兩個 int 數值元素出棧相加,然後將相加結果入棧)

7:bipush   10:將int類型的常量10壓入操作數棧    (這裏其實做了2步操作,第1從常量取值,第2放入操作數棧,所以序號7過了,就是9了,而沒有8,這2步在目前的jdk版本已經合併爲1步)

9:imul:執行int類型的乘法操作(在運行的時候將操作數棧中最接近棧頂的兩個 int 數值元素出棧相乘,然後將相加結果入棧)

10:istore_3:將int類型的變量3(30)存入局部變量

11:iload_3:將局部變量3(30)壓入操作數棧

12:ireturn:將int類型的數據返回

 

更多彙編指令

程序計數器

計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是 Native 方法,這個計數器的值則爲 (Undefined);線程私有的,上述反彙編語言的序號0、1、2 ...20都是理解爲程序計數器存的值,表示當前線程執行的位置,以便線程切換時候切換爲上次執行的地方繼續執行。

程序計數器特點

1 線程私有的,每個線程都有一個程序計數器.
2 是java虛擬機規範裏面, 唯一 一個 沒有規定任何 OutOfMemoryError 情況的區域
3 生活週期隨着線程的創建而創建,隨着線程的結束而消亡.
4 程序計數器是一塊較小的內存區域。

本地方法棧

本地方法棧則爲虛擬機使用到的 Native 方法服務,比如Thread底層會調用C的本地方法,java底層很多本地方法通過C來實現的。

圖3

 

每個方法在執行時都會創建一個棧幀(Stack Frame)用於存儲局部變量表操作數棧動態鏈接方法出口等信息。每一個方法從調用直至執行結束,就對應着一個棧幀從虛擬機棧中入棧到出棧的過程。(棧先進後出)

棧楨:

棧幀(Stack Frame)是用於支持虛擬機進行方法調用和方法執行的數據結構。它是虛擬機運行時數據區中的java虛擬機棧的棧元素。
棧幀存儲了方法的局部變量表、操作數棧、動態連接和方法返回地址等信息。
每一個方法從調用開始至執行完成的過程,都對應着一個棧幀在虛擬機裏面從入棧到出棧的過程。

局部變量表

每個棧幀中都包含一組稱爲局部變量表的變量列表,用於存放方法參數和方法內部定義的局部變量;

Java 虛擬機規範允許 Slot 的長度可以隨着處理器、操作系統或者虛擬機的不同而發生變化。對於64位的數據類型,虛擬機會以高位在前的方式爲其分配兩個連續的 Slot 空間。即 long 和 double 兩種類型。做法是將 long 和 double 類型速寫分割爲32位讀寫的做法。不過由於局部變量表建立在線程的堆棧上,是線程的私有數據,無論讀寫兩個連續的 Slot 是否是原子操作,都不會引起數據安全問題。

操作數棧:

操作數棧是一個後入先出(Last In First Out)棧,方法的執行操作在操作數棧中完成,每一個字節碼指令往操作數棧進行寫入和提取的過程,就是入棧和出棧的過程。操作數棧的每一個元素可以是任意的 Java 數據類型,32位數據類型所佔的棧容量爲1,64位數據類型所佔的棧容量爲2,在方法執行的任何時候,操作數棧的深度都不會超過在 max_stacks 數據項中設定的最大值(指的是進入操作數棧的 “同一批操作” 的數據類型的棧容量的和)。

動態鏈接

每個棧幀都包含一個指向運行時常量池(JVM 運行時數據區域)中該棧幀所屬方法屬性的引用,持有這個引用是爲了支持方法調用過程中的動態連接。

在 Class 文件格式的常量池(存儲字面量和符號引用)中存有大量的符號引用(1.類的全限定名,2.字段名和屬性,3.方法名和屬性),字節碼中的方法調用指令就以常量池中指向方法的符號引用爲參數。這些符號引用一部分會在類加載過程的解析階段的時候轉化爲直接引用(指向目標的指針、相對偏移量或者是一個能夠直接定位到目標的句柄),這種轉化稱爲靜態解析。另外一部分將在每一次的運行期期間轉化爲直接引用,這部分稱爲動態連接。

比如

 

方法出口:

在方法退出之後,都需要返回到方法被調用的位置,程序才能繼續執行,方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復它的上層方法的執行狀態。一般來說,方法正常退出時,調用者的程序計數器的值可以作爲返回地址,棧幀中很可能會保存這個計數器值。而方法異常退出時,返回地址是要通過異常處理器表來確定的,棧幀中一般不會保存這部分信息。

通俗點就是:方法退出的過程實際上就等同於把當前棧幀出棧,方法退出需要返回到方法被調用的位置,保證程序繼續執行。

元空間(方法區)

jdk1.8一以前叫做方法區,jdk1.8時候叫元空間,主要是存在類元信息(上述的JvmTest.class)、常量、靜態變量。

常量池詳解

Class文件中的常量池:

常量池主要用於存放兩大類常量:字面量(Literal)和符號引用量(Symbolic References),字面量相當於Java語言層面常量的概念,如文本字符串,聲明爲final的常量值等,符號引用則屬於編譯原理方面的概念,包括瞭如下三種類型的常量:

類和接口的全限定名
字段名稱和描述符
方法名稱和描述符

 

運行時常量池:

Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放。

      運行時常量池相對於Class文件常量池的另外一個重要特徵是具備動態性,Java語言並不要求常量一定只有編譯期才能產生,也就是並非預置入CLass文件中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中。

使用常量池的好處:

常量池是爲了避免頻繁的創建和銷燬對象而影響系統性能,其實現了對象的共享。
例如字符串常量池,在編譯階段就把所有的字符串文字放到一個常量池中。

(1)節省內存空間:常量池中所有相同的字符串常量被合併,只佔用一個空間。

(2)節省運行時間:比較字符串時,==(引用比較地址-基本比較值)比equals()(只能用於引用的比較,比較值)快。對於兩個引用變量,只用==判斷引用是否相等,也就可以判斷實際值是否相等。

     舉例:

 

 

  • String s1 =”123”;
  • String s2=new String (“123”);

  第一種我們之前已經見過了,這種方式聲明的字面量123是在編譯期就已經確定的,它會直接進入class文件常量池中;當運行期間在全局字符串常量池中會保存它的一個引用,實際上最終還是要在堆上創建一個”123”對象。
  第二種方式方式使用了new String(),也就是調用了String類的構造函數,我們知道new指令是創建一個類的實例對象並完成加載初始化的,因此這個字符串對象是在運行期才能確定的,創建的字符串對象是在堆內存上
  因此此時調用System.out.println(s0 == s1);返回的肯定是flase,因此==符號比較的是兩邊元素的地址,s1和s0都存在於堆上,但是地址肯定不相同。

堆內存結構

  • JVM內存劃分爲堆內存和非堆內存,堆內存分爲年輕代(Young Generation)、老年代(Old Generation

年輕代又分爲Eden和Survivor區。Survivor區由FromSpace和ToSpace組成。Eden區佔大容量,Survivor兩個區佔小容量,

默認比例是8:1:1。

  • 堆內存用途:存放的是對象,垃圾收集器就是收集這些對象,然後根據GC算法回收。

非堆內存用途:在JDK1.8版本廢棄了永久代,替代的是元空間(MetaSpace),元空間與永久代上類似,都是方法區的實現,

他們最大區別是:元空間並不在JVM中,而是使用本地內存。

分代概念

  • 新生成的對象首先放到年輕代Eden區,當Eden空間滿了,觸發Minor GC,存活下來的對象移動到Survivor0區,Survivor0區滿後觸發執行Minor GC,Survivor0區存活對象移動到Suvivor1區,這樣保證了一段時間內總有一個survivor區爲空。經過多次Minor GC仍然存活的對象移動到老年代。
  • 老年代存儲長期存活的對象,GC期間會停止所有線程等待GC完成,所以對響應要求高的應用盡量減少發生Major GC,避免響應超時。 Minor GC : 清理年輕代  Major GC : 清理老年代 Full GC : Full GC定義是相對明確的,就是針對整個新生代、老生代、元空間(metaspace,java8以上版本取代perm gen)的全局範圍的GC。

什麼情況下新生代的對象會被移入老年代?

第1種情況:對象年齡

JVM會給對象增加一個年齡(age)的計數器,對象每“熬過”一次GC,年齡就要+1,待對象到達設置的閾值(默認爲15歲)就會被移移動到老年代,可通過-XX:MaxTenuringThreshold調整這個閾值。

 

第2種情況:動態判斷

如圖上的A、B、D、E這四個對象,假如Survivor 2是100m,如果A + B + D的內存大小超過50m,現在D的年齡是10,那E都會被移動到老年代。實際上這個計算邏輯是這樣的:年齡1 + 年齡2 + 年齡n的多個對象總和超過Survivor區的50%,那就會把年齡n以上的對象都放入老年代。

堆內存常用參數

 

動態演示內存使用情況(VirtualVM插件)

 

(GC)垃圾回收器

爲啥要做垃圾回收?

如果不進行垃圾回收,內存遲早都會被消耗空,避免內存空間的浪費;空間是很寶貴的,使用有限的空間做最大的事。

怎麼找到需要回收的對象?

通過(GC)垃圾回收算法(引用計數器算法、可達性分析算法、標記清除算法、複製算法、標記整理算法、分代收集算法);

引用計數器算法

給對象中添加一個引用計數器,每當一個地方引用這個對象時,計數器加1;當引用失效時,計數器減1。任何時刻計數器爲0的對象就是不可能再被使用的。這種算法使用場景很多,

但是,Java中卻沒有使用這種算法,因爲這種算法很難解決對象之間相互引用的情況。(故java虛擬機不使用此類算法)

可達性分析法

通過一系列稱爲"GC Roots"的對象作爲起始點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈(即GC Roots到對象不可達時),則證明此對象是不可用的。(java採用此類算法)

GCRoots對象:

(1)虛擬機棧(棧幀中的局部變量區,也叫局部變量表)中引用的對象。

(2)方法區中的類靜態變量屬性引用的對象。

(3)方法區中常量引用的對象。

(4)本地方法棧中JNI(Native方法)引用的對象。

 

故:上圖中的 obj8、obj9、obj10都是不可達的,都是無用對象,當下次GC來的時候會回收此類對象。

可達性算法的4中引用關係:

(1)強引用

(2)弱引用

(3)虛引用

(4)軟引用

強引用:

強引用就是指在程序代碼之中普遍存在的,類似“Objectobj = new Object()”這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。

如果不使用時,可以賦值obj=null,顯示的設置obj爲null,則gc認爲該對象不存在引用,這時候就可以回收此對象。

弱引用:

弱引用描述非必需對象。被弱引用關聯的對象只能生存到下一次垃圾回收之前,垃圾收集器工作之後,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。Java中的類WeakReference表示弱引用。

軟引用:

軟引用用來描述一些還有用,但並非必需的對象。對於軟引用關聯着的對象,如果內存充足,則垃圾回收器不會回收該對象,如果內存不夠了,就會回收這些對象的內存。在 JDK 1.2 之後,提供了 SoftReference 類來實現軟引用。軟引用可用來實現內存敏感的高速緩存。軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。(可以用作圖片緩存,但是現在一般使用其他方式來實現緩存,如redis)

虛引用:

如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。

虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃 圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是 否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序如果發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。(用於JVM內存日誌監控)

 

標記清除算法

分爲“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,標記完成後統一回收所有被標記的對象。這種算法的不足主要體現在效率和空間,從效率的角度講,標記和清除兩個過程的的效率都不高;從空間的角度講,標記清楚後會產生大量不連續的內存碎片,內存碎片太多可能會導致以後程序運行過程中在需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發一次垃圾回收動作。

 

複製算法

複製算法是爲了解決效率問題而出現的,它將可用的內存分爲兩塊,每次只用其中的一塊,當這一塊內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已經使用過的內存一次性清理掉。這樣每次只需要對整個半區進行內存回收。(缺點1:太消耗內存,10G的內存只能使用5G,當然也可以配置比例,內存資源很寶貴的;缺點2:效率低下,老年代的對象存活率都很高,複製需要額外的時間空間)

 

標記整理算法

爲了解決複製算法引起的內存資源浪費以及效率低下問題提出了“標記-整理算法”,與標記-清除算法類似,讓所有存活對象都向一端移動,然後清理掉邊界以外的內存(可回收的對象)。

 

分代收集算法:(整合上述所有算法優點)

目前虛擬機基本都採用分代收集算法來進行垃圾回收。這種算法結合了以上的內容,根據對象的生命週期的不同將內存劃分爲幾塊,然後根據各塊的特點採用最適當的收集算法。大批對象死去、少量對象存活的(新生代),使用複製算法,複製成本低;對象存活率高、沒有額外空間進行分配擔當的(老年代),採用標記-清理算法或者標記-整理算法。

 

垃圾收集器分類

垃圾收集器就是垃圾回收的具體體現。不同虛擬機所提供的垃圾收集器可能會有很大差別,HotSpot垃圾收集器如下圖:

 

Serial收集器

採用複製算法的單線程的收集器,單線程一方面意味着他只會使用一個CPU或者一條線程去完成垃圾收集工作,另一方面也意味着他進行垃圾收集時必須暫停其他線程的所有工作。簡單而高效。(虛擬機運行在Client模式下的默認新生代收集器)

Parnew收集器

Serial收集器的多線程版本,Server模式下的虛擬機首選的新生代收集器。

Parallel Scavenge收集器

也採用了複製算法,也是並行的多線程收集器。CMS等收集器的關注點是儘可能縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量。所謂吞吐量就是CPU用於運行用戶代碼時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)。Parallel Scavenge收集器是虛擬機運行在Server模式下的默認垃圾收集器。(吞吐量優先)

Serial Old收集器

Serial收集器的老年代版本,同樣是一個單線程收集器,使用==“標記-整理算法”,(虛擬機運行在Client模式下的默認老年代收集器)

Parallel Old收集器

Parallel Scavenge收集器的老年代版本,使用多線程和==“標記-整理算法”==。(吞吐量優先)

CMS收集器

收集器是以獲取最短回收停頓時間爲目標的收集器。使用標記-清除算法,收集過程分爲如下四步:(1)初始標記,標記GCRoots能直接關聯到的對象,時間很短。(2)併發標記,進行GCRoots Tracing(可達性分析)過程,時間很長。(3)重新標記,修正併發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,時間較長。(4)併發清除,回收內存空間,時間很長。其中,併發標記與併發清除兩個階段耗時最長,但是可以與用戶線程併發執行。

G1收集器

G1是目前技術發展的最前沿成果之一,HotSpot開發團隊賦予它的使命是未來可以替換掉JDK1.5中發佈的CMS收集器。與其他GC收集器相比,G1收集器具有以下特點:
(1)併發和並行。使用多個CPU來縮短Stop The World停頓時間,與用戶線程併發執行。

(2)分代收集。獨立管理整個堆,但是能夠採用不同的方式去處理新創建對象和已經存活了一段時間、熬過多次GC的舊對象,以獲取更好的收集效果。

(3)空間整合。基於標記-整理算法,無內存碎片產生。

(4)可預測的停頓。能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片段內,消耗在垃圾收集器上的時間不得超過N毫秒。

在G1之前的垃圾收集器,收集的範圍都是整個新生代或者老年代,而G1不再是這樣。使用G1收集器時,Java堆的內存佈局與其他收集器有很大差別,它將整個Java堆劃分爲多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分(可以不連續)Region的集合。

解析器和編譯器

Java Interpreter(Java 解釋器)
Java編譯器生成的是與機器碼不同的java字節碼,並不能被硬件中的CPU直接執行。而java解釋器就像植根於軟件中的CPU,能夠解析並執行java字節碼。(字節碼解析成彙編指令)

Java Compiler (Java 編譯器)
Java編譯器讀取java源文件(*.java)並將它們編譯爲java字節碼文件(*.class)。

 

 

 

 

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