JVM 整体结构

331 阅读5分钟

概述

Java内存区域.png Jvm包含两个子系统和两个组件,两个子系统为Class Loader(类加载系统)和Execution engine(执行引擎);两个组件Runtime data area(运行时数据区), Native Interface(本地接口)。

  1. Class Loader类加载:根据给定的类名(如:java.lang.Object)来装载class文件到Runtime data area中的方法区。
  2. 执行引擎:执行calsses中的指令
  3. 本地接口:与本地方法库交互,与其他编程语言交互的接口
  4. 运行时数据区:就是我们说的JVM内存

基本流程: 首先编译器把Java代码转换成字节码,类加载器(Class Loader)将字节码加载到内存中,将其放在运行时数据区的方法区,字节码是JVM的一套指令集规范,并不是交给底层操作系统去执行,因此需要特定的命令解析器执行引擎,将字节码翻译成底层的操作系统指令,在交有CPU去执行,而这个过程需要调用其他语言本地接口来实现整个程序的功能。

1. 类加载子系统

1.1 JVM 类加载机制

Jvm类加载机制分为5个部分,加载,验证,准备,解析,初始化

image.png

加载

加载是类加载过程中的一个阶段, 这个阶段会在内存中生成一个代表这个类的 java.lang.Class 对象, 作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个 Class 文件获取,这里既可以从 ZIP 包中读取(比如从 jar 包和 war 包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将 JSP 文件转换成对应的 Class 类)。

验证

这一阶段的主要目的是为了确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备

准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。

解析

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。

初始化

初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由 JVM 主导。到了初始阶段,才开始真正执行类中定义的 Java 程序代码。

1.2 类加载器ClassLoader

1.2.1 类加载器有哪些?

主要有一下四种类加载器:

  • 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
  • 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。

1.2.2 什么是双亲委派模型

双亲委派模型

如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

image.png

1.2.3 打破双亲委派模型

打破双亲委派机制则不仅要继承ClassLoader类,还要重写loadClass和findClass方法。

2. 运行时数据区

2.1 程序计数器(Program Counter Register)

当前线程所执行字节码的行号指示器,字节码的解析工作是通过改变这个计数器的值,来选取下一条要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能,都要依赖这个技术器来完成。

2.2 虚拟机栈(Java Virtual Machine Stacks)

用于存储局部变量表,操作数栈,动态链接,方法出口等信息。

2.3 本地方法栈(Native Method Stack)

与虚拟机栈的作用一样,只不过虚拟机栈调用的时Java方法,本地方法栈调用的时Native方法。

2.4 方法区(Method area)

用于存储虚拟机加载的类的信息,常量,静态变量,即时编译后代码等数据。

2.5 堆(Java Heap)

Java虚拟机中内存最大的一块,是被所有线程所共享的,几乎所有对象的实例都是在这里分配内存的。当然也有列外后面会详细说明。

虚拟机栈

线程私有,是每个方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用完到执行完成的过程,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。

3. 执行引擎

3.1 概述

虚拟机字节码执行引擎

3.2 运行时栈帧结构

栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking) 方法返回值和异常分派。栈帧随着方法调用而创建,随着方法结束而销毁-无论方法正常结束还是异常中断都算方法结束。

image.png

3.3 基于栈的字节码执行引擎

通过一个例子,来看看执行引擎是如何执行代码的。

public class Test {

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

    public static void main(String[] args) {
        Test test = new Test();
        int result = test.math();
    }
}

执行本地编译字节码命令 D:\me\leetCode\src\main\java>javac Test.java D:\me\leetCode\src\main\java>javap -c -l Test > Test.txt 对照Jvm基本指令可以看出程序执行的过程。

image.png

参考资料:

《深入理解Java虚拟机》

《疯狂创客:Java面试题》