Java虚拟机入门

Java虚拟机的工作原理, 大致可以分为以下步骤: 首先Java源文件(.java文件)经过编译器(javac命令等)将代码编译为字节码文件(.class文件), 然后加载字节码文件, 载入系统分配给JVM的内存区, 然后执行引擎解释或编译类文件, 再由即时编译器将字节码转化为机器码.

1. JVM运行时数据区

JVM运行时数据区

1.1 方法区

属于共享内存区域. 存储被JVM加载的类信息、常量、静态变量、编译后的代码等数据。

1.2 堆内存

堆内存可以细分为: 老年代, 新生代. 在JVM启动时创建, 存放对象的实例, 对于大多数应用来说,这块区域是JVM所管理的内存中最大的一块.

1.3 虚拟机栈

每一个线程在虚拟机栈中有一个私有的空间, 在每一个方法执行时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息. 每一个方法从调用直至执行结束, 就对应着一个栈帧从虚拟机栈中入栈到出栈的过程.

1.4 本地方法栈

和虚拟机栈功能类似, 不同的是本地方法栈是为虚拟机使用native本地方法而准备的.

1.5 程序计数器

记录当前线程执行字节码的位置, 存储的是字节码指令地址, 如果执行native方法, 计数器值为空. jvm通过改变这个计数器的值来获取下一条需要执行指令的字节码指令.

2. class文件解析

public class Demo1 {
    public static void main(String[] args) {
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}

创建一个类Demo1, 执行javac进行编译获取class文件, 之后执行javap -v Demo1.class > Demo1.txt进行class文件解析.

2.1 版本号
public class Demo1
  minor version: 0 // 次版本号
  major version: 52 // 主版本号, 表示jdk8. 5, 6, 7, 8分别对应49, 50, 51, 52
  flags: ACC_PUBLIC, ACC_SUPER // 访问标志

打开txt文件可以找到以上内容. 其中访问标志可以查看以下内容.
访问标志

2.2 常量池
Constant pool:
   #1 = Methodref          #5.#14         // java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #17.#18        // java/io/PrintStream.println:(I)V
   #4 = Class              #19            // Demo1
   #5 = Class              #20            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               main
  #11 = Utf8               ([Ljava/lang/String;)V
  #12 = Utf8               SourceFile
  #13 = Utf8               Demo1.java
  #14 = NameAndType        #6:#7          // "<init>":()V
  #15 = Class              #21            // java/lang/System
  #16 = NameAndType        #22:#23        // out:Ljava/io/PrintStream;
  #17 = Class              #24            // java/io/PrintStream
  #18 = NameAndType        #25:#26        // println:(I)V
  #19 = Utf8               Demo1
  #20 = Utf8               java/lang/Object
  #21 = Utf8               java/lang/System
  #22 = Utf8               out
  #23 = Utf8               Ljava/io/PrintStream;
  #24 = Utf8               java/io/PrintStream
  #25 = Utf8               println
  #26 = Utf8               (I)V

继续往下阅读, 可以看到类信息包含的常量池.
常量池

2.3 构造方法
public Demo1();
    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 1: 0

接续往下阅读, 可以看到类的构造函数. 在Demo1中, 我们并没有写构造函数, 由此可见, 没有定义构造函数时, 会有隐式的无参构造函数.

2.4 main方法

 main方法
接续往下阅读, 可以看到程序的入口main方法. 具体的说明可以查看图片中的注释. 具体的指令可以自行查阅.

2.5 程序执行过程

程序执行过程

3. 总结

本文主要将JVM运行的核心逻辑进行了解析, 需要注意的是, JVM运行原理中更底层的实现, 针对不同的操作系统会有不同的实现, 这也是JAVA能实现一次编写, 处处运行的原因.

posted on 2021-02-22 21:04  annwyn  阅读(124)  评论(0)    收藏  举报

导航