最近看了点Java虚拟机方面的内容,总结一下,水平有限,有错误欢迎指出。
Java程序执行过程
先来了解下Java程序的整个执行过程。
Java是一种编译型和解释型的混合语言,Java文件先被编译成与平台无关、JVM能够读懂的字节码文件(class文件),再由JVM转换成特定平台的机器码进行执行,从而实现了跨平台。
例如现在有一个Main.java文件
1. 编译
在命令行窗口javac Main.java
,将会生成Main.class文件
2. 执行
java Main
后,会进入上图红框中的部分。类加载器会将class文件字节码读取进来,将其中的信息放入“运行时数据区”,然后执行引擎解释,转换为特定平台的CPU机器码,CPU执行机器码,从而完成程序的整个执行。
类加载器
类加载器工作过程主要分为加载、链接、初始化
Loading(加载)
Java中有许多类加载器,它们构成层级关系。加载类时,会从底向上的依次去各个加载器的库中找寻有没有这个类。如果一直没有找到,则再从顶向下的去各自的路径下去找,来尝试加载。如下图
Linking(链接)
- Verifying:检查载入的类文件是否符合Java规范和虚拟机规范。
- Preparing:为这个类分配所需要的内存,确定这个类的属性、方法等所需的数据结构。(Prepare a data structure that assigns the memory required by classes and indicates the fields, methods, and interfaces defined in the class.)
- Resolving:将该类常量池中的符号引用都改变为直接引用。
需要注意的是,Java是动态链接。C和C++都是编译期就已经链接好了,而Java是等到需要这个类时才开始加载并链接。
Initialing(初始化)
Initialing:初始化类的局部变量,为静态域赋值,同时执行静态初始化块。
总结:
类加载器其实就是根据编译后的class文件,将java字节码载入JVM内存,并完成对运行数据处于的初始化工作,供执行引擎执行。
运行时数据区域
运行时数据区域,即Java运行时的内存区域。
从图中可以看出,主要分为六个模块:
PC计数器,JVM栈,本地方法栈,堆,方法区,运行时常量区(其实是方法区的一部分,只是因为很重要,被单独提出来了)
线程独自拥有
PC计数器,JVM栈,本地方法栈这个三个区域都是线程独自拥有的,每个线程都会单独创建,彼此不互相影响
- PC计数器
PC寄存器的内容总是下一条将被执行指令的“地址”,这里的“地址”可以是一个本地指针,也可以是在方法字节码中相对于该方法起始指令的偏移量 - JVM栈
以帧为单位,每当一个方法被调用时,就会压入一个栈帧入栈。栈帧中存放了方法的局部变量,中间运算结果,以及常量池的引用(见下图)。每当一个方法结束时栈帧便会弹出。
- 本地方法栈
即Java通过JNI调用其他语言的方法(本地方法)时,所用到的栈
线程共享
堆,方法区,运行时常量区这三个区域是线程共享的
- 堆
对象和数组存放的区域。所有的Java程序共享一个堆空间。Java垃圾回收机制就是针对此区域工作。 - 方法区
当一个class文件被加载时,此类的类型信息会被提取存储在方法区。包括该类的:运行时常量池、有关域和方法的信息、静态变量、类和方法的字节码。 - 运行时常量池
存放加载类类型的:直接常量,方法和域的引用(Java对象运行时的内存结构中提到的方法表)。当一个方法或者域被引用的时候,JVM就通过运行常量池中的这些引用来查找方法和域在内存中的的实际地址。
本文内容部分引用:
Java虚拟机工作原理详解
JAVA虚拟机体系结构