JDK的体系结构图:
Java程序的执行过程:
JVM的大致结构:
1.类装载子系统 2.字节码执行引擎 3.运行时数据区
接下里以Math测试类为例进行分析:
1.在运行程序之后,JVM会在栈内存区域中分配一小块区域给当前线程使用:
(意味着栈会为每一个线程分配一块独有的区域)
2.在运行程序中的main方法之后,系统会分配给main方法一块内存区域(栈帧),用来存放该方法独有的局部变量等数据,然后若系统调用其他方法(如例中compute方法),则也为其分配一段栈帧,然后进栈(如图),若该方法执行结束之后,则该栈帧出栈,释放掉相关资源;
为了更好的理解JVM的工作流程,我们来观察该程序底层的指令码:
首先看compute方法:
依靠JVM指令手册翻译相关的操作含义:
iconst_1: 将int型的常量1压入操作数栈
istore_1: 将int型值存入局部变量1
iconst_2: 将int型的常量2压入栈
istore_2: 将int型值存入局部变量2
iload_1: 从局部变量1中装载int类型值
iload_2: 从局部变量2中装载int类型值
iadd: 执行int类型的加法(从操作数栈中弹出两个操作数进行操作)
bipush 10:将一个8位带符号整数压入栈
imul: 执行int类型的乘法(同理:弹两个元素)
istore_3: 将int型值存入局部变量3
iload_3: 从局部变量3中装载int类型值
ireturn: return
若局部变量表中存放的是对象,则此时存放的是该对象在堆中的地址(对象的引用)
程序计数器(每个线程独有的):
记录了当前线程正在运行的那行JVM指令码的行号(位置)
由字节码执行引擎修改程序计数器的值;
为什么要记录程序计数器的值呢?
为了方便线程切换时产生的中断返回,记录接下来要执行指令的位置;
方法出口:
记录要转回方法中的位置(也就是方法调用结束后的位置)
方法区:
常量、静态变量(如下图中的两个)、类信息(Math.class)
若静态变量为一个对象,则此时方法区中存放的也是该对象在堆中的地址(对象的引用)
方法区在jdk1.8之前叫永久带,1.8之后叫做元空间;
本地方法栈:用来存放该线程中调用的本地方法(用native修饰的方法,用C、C++或其他语言实现的);