精通虚拟机栈底层原理

精通虚拟机栈底层原理

  • 虚拟机栈 VM Stack
    • java1.8之前JVM(Java Virtual Machine)内存模型分别是:
    • 版本迭代
    • 特性
    • 结构
    • 查看方式
    • 字节码文件是什么结构?
    • 什么是栈帧?
    • 栈帧的内部结构有哪些?

虚拟机栈 VM Stack

本文重点讲解虚拟机栈

java1.8之前JVM(Java Virtual Machine)内存模型分别是:

堆 Heap
虚拟机栈 VM Stack
本地方法栈 Native Method Stack
方法区 Method Area
程序计数器 Program Counter Register

版本迭代

Java1.4 以及之前 虚拟机栈大小默认为256k
Java1.5 以后 虚拟机栈默认大小为1M
可以通过命令:
-Xss3M 设置虚拟机栈大小

特性

  1. 虚拟机栈申请的内存不是JVM虚拟机中的内存,是向操作系统申请内存的
  2. 每个线程创建时都会创建虚拟机栈,多个虚拟机栈互相不可见
  3. 虚拟机栈是一种先入先出栈(FIFO)

结构

一个线程对应一个虚拟机栈,虚拟机栈中有多个栈帧(Stack Frame)

查看方式

jclasslib 传送门:↓下面这段话非常重要 download
想看局部变量表需要 加 -g 命令 如:
javac -g HelloWorld.java

字节码文件是什么结构?

  1. Constant Pool 常量池表: 常量池表中信息是class文件所有符号引用,这些符号引用会在一定时刻被替换为直接引用(后文详细讲解)
  2. Interfaces 接口集: 类的实现接口集
  3. Fields 成员变量表: 静态变量、常量
  4. Methods 成员方法: 程序员书写的方法,如果程序员未指定构造方法,java虚拟机在类加载时会给class文件创建< init > 方法,如果是实例对象,实例对象的方法的局部变量表中0位索引 (也称槽位 Solt)默认是所属对象的引用“this” 后续会做讲解。如果类中有静态属性 虚拟机会创建< clinit >方法在类初始化阶段 给类中的静态成员赋值,这里区别一下 类在准备阶段会赋予默认值,这个方法会赋予程序员指定的值
  5. Attributes 属性集: 本类,内部类信息

什么是栈帧?

栈帧就是供虚拟机进行方法的调用和方法的执行,当调用一个方法时代表这个方法的入栈,当执行引擎执行这个方法时,代表着这个栈帧出栈

栈帧的内部结构有哪些?

1. 局部变量表: 局部变量表存储着方法的参数和局部变量,如果是实例对象中的方法那么方法局部变量表中槽位 0 是当前对象的引用 this,这个this是隐含参数。首先分配参数列表到局部变量表,然后根据方法内的变量顺序和作用域分配其余的变量,槽位是可重用的,请看如下代码:

	public void method1(){
		{
			int x = 0;
			System.out.println(x);
		}
		int y = 0;
	}

x的作用域只在局部代码块,而y在x变量的下方,所以x和y可以共用一个槽位,在书写代码是可以通过此种方法将一次性变量声明在局部代码块中起到优化的作用,4字节数据类型栈1大小的槽位,4字节以上的占2大小槽位,例如:

	public static void method2(){
		int i = 0;
		long j = 0;
		int k = 0;
		...
	}

由于这个方法不是实例对象中方法,所以0槽位变量不是this,该方法的局部变量表如下:
0 槽位是 i
1 槽位是 j
3 槽位是 k

2. 操作数栈: 也称操作栈,是一个后入先出占(LIFO)操作数栈的每一个元素可以是任意java类型,32位虚拟机 数据类型所占栈容量为1,64位为2,操作栈容量的单位为“字宽”,对32位虚拟机来说一个字宽为4,64位为8,当一个方法执行时,这个方法的操作数栈是空的,在方法执行过程中会有各种字节码指向操作数栈中写入和提取值,如int 1=0; i=i+1;(入栈)System.out.println(i);(出栈)

3. 动态连接: 操作数栈的每一个元素可以是任意java类型,32位虚拟机 数据类型所占栈容量为1,64位为2,操作栈容量的单位为“字宽”,对32位虚拟机来说一个字宽为4,64位为8,当一个方法执行时,这个方法的操作数栈是空的,在方法执行过程中会有各种字节码指向操作数栈中写入和提取值,如int 1=0; i=i+1;(入栈)System.out.println(i);(出栈)
4. 方法返回地址: 当一个方法被执行有两种方式推出这个方法,第一种方式是执行引擎执行到方法返回的字节码指令,这时候可能会有返回值传递给方法调用者,是否有返回值和返回值的类型是根据遇到的返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)
另一种退出方式是出现异常,无论是虚拟机异常还是使用throw(字节码指令是athrow)产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,也会导致方法退出,这种退出称为异常完成出口(Abrupt Method Invocation Completion)
一个方法发使用异常完成退出是不会给它的调用者产生任何返回值
无论采用什么方式退出在方法退出之前,都需要返回到方法被调用的位置,程序才能继续运行,方法退出时,调用者程序计数器的值可以作为返回地址,当前方法栈帧中很可能会保存了这个地址,而方法异常退出时返回地址时要通过异常处理器的类型来决定的,栈帧中一般不会保存这个信息,所以方法异常完成出口没有任何返回值
方法的退出相当于当前方法的栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈(为的是恢复方法原来的模样),把返回值压入调用者的操作数栈中,为了使程序继续会调用程序计数器的值以指向方法调用指令执行完下一条指令

5. 附加方法信息: 虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的

你可能感兴趣的:(Java)