Skip to content

一、JVM 运行时内存结构

1. Heap 堆

  • Heap 堆
    • 含义:主要将 存储对象实例 到内存中, 为 对象实例开辟内存空间
    • 作用:解决的是 数据存储问题。(即数据怎么放、放哪儿)
    • 是否共享:线程(数据)共享,存在线程安全问题。

例如:

java
String s1 = new String("你好!我是S1");
String s2 = new String("你好!我是S2");
List<String> l3 = Stream.of("你好!我是l2").collect(Collectors.toList());
  • 方法区(元空间)
    • 含义:主要存放类的信息
    • 类的信息主要包含:类的信息局部变量表方法信息常量池
    • 线程安全问题:线程(数据)共享,存在线程安全问题。

例如:

  • MetaData.java 示例代码, 运行代码获取到 MetaData.class
java
package com.calvin.jvm.structure;

import com.sun.org.apache.xerces.internal.impl.xpath.regex.Match;

/**
 * 方法区(元空间)
 *
 * @author Calvin
 * @date 2023/5/11
 * @since v1.0.0
 */
public class MetaData {

    /**
     * 静态常量
     */
    public static final Integer I = 100;

    /**
     * 字符串常量
     */
    public final String A = "a";

    /**
     * 常量
     */
    public final double b = Math.random();

    /**
     * 主方法信息
     *
     * @param args 参数
     */
    public static void main(String[] args) {
        // 局部: 字符串常量
        String c = "c";
        System.out.println(c);
    }
}
  • 通过 java 命令,查看 MetaData.class 汇编指令。
shell
$ javap -c -v  MetaData.class
  • MetaData.class 汇编指令, 文件如下:
C#
Classfile /Users/calvin/学习/Jvm 虚拟机/calvin-java-jvm/chapter-02-jvm-structure/target/classes/com/calvin/jvm/structure/MetaData.class
  Last modified 2023-5-11; size 919 bytes
  MD5 checksum 8946635fee16f950d951859aeeb22656
  Compiled from "MetaData.java"

// 类的信息
public class com.calvin.jvm.structure.MetaData
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER


// 常量池
Constant pool:
   #1 = Methodref          #12.#35        // java/lang/Object."<init>":()V
   #2 = String             #36            // a
   #3 = Fieldref           #11.#37        // com/calvin/jvm/structure/MetaData.A:Ljava/lang/String;
   #4 = Methodref          #38.#39        // java/lang/Math.random:()D
   #5 = Fieldref           #11.#40        // com/calvin/jvm/structure/MetaData.b:D
   #6 = String             #31            // c
   #7 = Fieldref           #41.#42        // java/lang/System.out:Ljava/io/PrintStream;
   #8 = Methodref          #43.#44        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #9 = Methodref          #45.#46        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  #10 = Fieldref           #11.#47        // com/calvin/jvm/structure/MetaData.I:Ljava/lang/Integer;
  #11 = Class              #48            // com/calvin/jvm/structure/MetaData
  #12 = Class              #49            // java/lang/Object
  #13 = Utf8               I
  #14 = Utf8               Ljava/lang/Integer;
  #15 = Utf8               A
  #16 = Utf8               Ljava/lang/String;
  #17 = Utf8               ConstantValue
  #18 = Utf8               b
  #19 = Utf8               D
  #20 = Utf8               <init>
  #21 = Utf8               ()V
  #22 = Utf8               Code
  #23 = Utf8               LineNumberTable
  #24 = Utf8               LocalVariableTable
  #25 = Utf8               this
  #26 = Utf8               Lcom/calvin/jvm/structure/MetaData;
  #27 = Utf8               main
  #28 = Utf8               ([Ljava/lang/String;)V
  #29 = Utf8               args
  #30 = Utf8               [Ljava/lang/String;
  #31 = Utf8               c
  #32 = Utf8               <clinit>
  #33 = Utf8               SourceFile
  #34 = Utf8               MetaData.java
  #35 = NameAndType        #20:#21        // "<init>":()V
  #36 = Utf8               a
  #37 = NameAndType        #15:#16        // A:Ljava/lang/String;
  #38 = Class              #50            // java/lang/Math
  #39 = NameAndType        #51:#52        // random:()D
  #40 = NameAndType        #18:#19        // b:D
  #41 = Class              #53            // java/lang/System
  #42 = NameAndType        #54:#55        // out:Ljava/io/PrintStream;
  #43 = Class              #56            // java/io/PrintStream
  #44 = NameAndType        #57:#58        // println:(Ljava/lang/String;)V
  #45 = Class              #59            // java/lang/Integer
  #46 = NameAndType        #60:#61        // valueOf:(I)Ljava/lang/Integer;
  #47 = NameAndType        #13:#14        // I:Ljava/lang/Integer;
  #48 = Utf8               com/calvin/jvm/structure/MetaData
  #49 = Utf8               java/lang/Object
  #50 = Utf8               java/lang/Math
  #51 = Utf8               random
  #52 = Utf8               ()D
  #53 = Utf8               java/lang/System
  #54 = Utf8               out
  #55 = Utf8               Ljava/io/PrintStream;
  #56 = Utf8               java/io/PrintStream
  #57 = Utf8               println
  #58 = Utf8               (Ljava/lang/String;)V
  #59 = Utf8               java/lang/Integer
  #60 = Utf8               valueOf
  #61 = Utf8               (I)Ljava/lang/Integer;
{
  public static final java.lang.Integer I;
    descriptor: Ljava/lang/Integer;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public final java.lang.String A;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_FINAL
    ConstantValue: String a

  public final double b;
    descriptor: D
    flags: ACC_PUBLIC, ACC_FINAL

  public com.calvin.jvm.structure.MetaData();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String a
         7: putfield      #3                  // Field A:Ljava/lang/String;
        10: aload_0
        11: invokestatic  #4                  // Method java/lang/Math.random:()D
        14: putfield      #5                  // Field b:D
        17: return
      LineNumberTable:
        line 12: 0
        line 22: 4
        line 27: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      18     0  this   Lcom/calvin/jvm/structure/MetaData;

  // 方法信息
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: ldc           #6                  // String c
         2: astore_1
         3: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: aload_1
         7: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        10: return
      LineNumberTable:
        line 36: 0
        line 37: 3
        line 38: 10

      // 局部变量表
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  args   [Ljava/lang/String;
            3       8     1     c   Ljava/lang/String;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        100
         2: invokestatic  #9                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         5: putstatic     #10                 // Field I:Ljava/lang/Integer;
         8: return
      LineNumberTable:
        line 17: 0
}
SourceFile: "MetaData.java"
  • Stack 虚拟机栈
    • 含义:主要支持虚拟机 进行方法调用方法执行 的数据结构。
    • 作用:解决的是 运行问题。(即程序如何执行,或者如何处理数据)
    • 栈数据结构原则:先进后出,后进先出
    • 栈帧的内部结构主要包含:
      • 局部变量表
      • 操作数栈
      • 动态链接
      • 方法出口

例如:

  • StackFrame.java 示例代码, 运行如下代码。
java
package com.calvin.jvm.structure;

/**
 * 栈帧
 *
 * @author Calvin
 * @date 2023/4/25
 * @since v1.0.0
 */
public class StackFrame {


    /**
     * B 方法
     */
    public void b() {
        System.out.println("我是B方法");
    }

    /**
     * A 方法
     */
    public void a() {
        b();
        System.out.println("我是A方法");
    }

    /**
     * 主方法
     *
     * @param args 参数
     */
    public static void main(String[] args) {
        new StackFrame().a();
    }

}
  • 输出结果
log
我是B方法
我是A方法
  • Native 本地方法栈
    • 含义:使用 Java 代码调用 C 语言(Java Native Interface), 简称 JNI。
  • 程序计数器(PC寄存器)
    • 含义: 在多线程情况下,线程上下文切换时,记录 线程执行行号

3. 执行引擎

  • 执行引擎(Execution Engine):
    • 4 大部分: 解释器(Interpreter)及时编译器(JIT Compiler)分析器(Profiler)垃圾回收器(Garbage Collection)