时隔一年,兜兜转转又转回Android开发,在开发过程中,对JAVA虚拟机内容有点疑惑,于是翻看了很多博客,根据自己的理解在此记录总结下,以便后续复习,非常感谢各位博主的辛勤付出。

一、JVM

JVM即java虚拟机,对于虚拟机有过了解的同学都知道虚拟机实际上是通过虚拟计算机硬件来运行一个系统的,实现相应的功能。jvm既然被称为java虚拟机,那么它必然也有相应的结构来完成java代码的执行(详见java虚拟机体系结构图)。而且JVM有一套自己的指令集,以及一套class文件的规范,凡是符合该规范的class文件都可以被jvm执行。

同时,JVM会根据不同平台将字节码文件(即class文件)解释成为特定的机器码,从而实现java跨平台特性。

1.1 JAVA虚拟机的体系结构图:

java虚拟机底层原理 java虚拟机原理图解_体系结构


图片来源于:

上图中主要描述一个java文件的执行流程,即:

  1. java文件被编译器编译成class文件
  2. class文件被类装载器加载到内存中的方法区
  3. JVM执行引擎读取方法区的字节码进行执行(即:解析执行、编译执行或者两者兼有)
  4. 输出执行结果

1.2 jvm内存结构与操作系统内存布局进行类比说明:

java虚拟机底层原理 java虚拟机原理图解_java虚拟机底层原理_02


从图中可以看出,jvm是类比操作系统内存布局进行设计的。根据上图中总结如下:

  1. jvm内存是存在于计算机内存中,并根据自身需求将内存划分为:pc寄存器、虚拟机栈、本地方法栈、jvm堆以及方法区,这些区域共同组成了运行时数据区,其中gc主要发生在堆区,栈由jvm自行管理
  2. pc寄存器:其主要职责是指示线程要执行的下一条指令的地址,具体执行过程参考下文:Class文件的执行过程
  3. 栈:主要是有栈帧组成,每个栈帧代表一个方法,且位于栈顶的栈帧是当前正在执行的方法。
  4. 堆:java中一切皆对象,所有对象都是在堆中生成,有gc垃圾回收器进行回收
  5. 方法区:类似计算机的硬盘,主要存放class文件,其中class文件基本组织结构可以参考Class文件的内存信息

1.3 运行时数据区图:

java虚拟机底层原理 java虚拟机原理图解_体系结构_03


从图中可以看出:方法区和堆是线程共享的,虚拟机栈、本地方法栈和程序计数器都是线程私有的,这里的程序计数器是线程私有的是因为线程可以被切换暂停的,当恢复运行时由程序计数器执行接着执行的指令地址。

二、Class文件的执行过程

前人栽树后人乘凉,这里同样引用用一段简单代码看JVM的执行过程一文的内容来分析jvm的执行过程。这部分内容是基于栈的指令集的,与基于寄存器的指令集是不同是,学过编译原理的会更清楚具体的执行流程。简单的说每部操作都在操作数栈中进行压栈和出栈执行的。

package cn.com;

public class SeeJvm {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int c = (a+b)*10;
    }
}

main方法创建了一个线程,它的字节码指令分别为:

java虚拟机底层原理 java虚拟机原理图解_JVM_04

运行时数据区状态如下图:

java虚拟机底层原理 java虚拟机原理图解_内存模型_05


栈桢包括存储局部变量区、操作数栈、动态连接、方法出入口等信息, 栈的操作基本原则是先进后出,所以1先从局部变量区入栈,2后入栈,idd指令就是将1,2弹出栈相加将结果保存在局部变量区后再次入栈,在开始方法执行前,pc存储器的指针是第一条指令的地址,局部变量区和操作数栈都没有数据,第1条到第4条指令就是将常数1,2分别存储到局部变量区

java虚拟机底层原理 java虚拟机原理图解_内存模型_06


第5和第6条命令又将常数1,2再次入栈:

java虚拟机底层原理 java虚拟机原理图解_体系结构_07


第7条指令将栈顶两个元素弹出相加再次入栈:

java虚拟机底层原理 java虚拟机原理图解_java虚拟机底层原理_08


同理,第8条指令将10入栈,当前pc寄存器的地址是9:

java虚拟机底层原理 java虚拟机原理图解_内存模型_09


然后将第10步将10和3同时弹出相乘,并把结果压入栈中:

java虚拟机底层原理 java虚拟机原理图解_体系结构_10


第10条指令完成后,第11条指令将30这个结果存入局部变量区。

java虚拟机底层原理 java虚拟机原理图解_内存模型_11


第12条指令是return,这个方法执行完后对应的这些线程私有的部件都会被jvm回收,局部变量区的所有值将全部释放,pc寄存器会被销毁,在Java栈中与这个方法对应的栈桢将会消失,到此这一段代码的执行过程就演示完了,看起来也没有想象中那么复杂。这里我开始有一个问题:为什么JVM要采取基于栈的结构方式,变量计算时直接从内存中拿不是节约几次入栈操作吗?

后来查资料来看一是为了保证在没有或者很少有寄存器的机器上也能保证Java代码的正确性,这是JVM平台无关性的前提。

当时在阅读此文章的时候有点疑惑:如果将a+b单独抽出来做为一个方法(假设方法名为:add)其执行过程会怎么样呢?具体过程如下:

  1. 创建一个add方法的栈帧,并压入jvm栈中
  2. 执行add方法
  3. 根据add方法的返回地址,将执行结果压人main方法的栈帧中的操作数栈,并调整main方法的程序计数器
  4. 继续执行mian方法栈帧中的指令,也就是接着执行乘10操作指令