虚拟机栈
虚拟机栈为每个线程所私有的,如下图:
栈帧是什么?
栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址
等信息
栈内存为线程私有的空间,每个方法在执行时都会创建一个栈帧,执行该方法时,就会将该方法所对应的栈帧入栈
-
局部变量表:用于存储方法参数和定义在方法体内部的局部变量,局部变量表的容量在编译器就被确定下来
-
操作数栈:在方法执行过程中,根据字节码指令,往栈中写入数据或者提取数据,即入栈、出栈
-
动态链接:每一个栈帧内部都包含一个指向
运行时常量池
中该栈所属方法的引用,包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接,在 Java 源代码被编译为字节码文件时,所有变量和方法都为符号引用保存在 class 文件的常量池,动态链接的作用就是将这些符号引用转换为调用方法的直接引用
-
方法返回地址:无论哪种方法,在方法退出后都该回到该方法被调用的位置,因此
方法返回地址
存储调用者的 pc 计数器的值
这里说一下符号引用和直接引用的区别?
符号引用的字面量明确定义在 《Java虚拟机规范》 的 Class 文件格式中
直接引用就是直接指向目标的指针、相对偏移量或简介定位到目标的句柄
栈会溢出吗?
虚拟机栈会溢出的,如果方法调用的过多,导致栈中压入的栈帧太多,就会出现 栈溢出错误
- 如果线程请求的栈深度大于虚拟机所允许的深度(Xss默认1m),会抛出 StackOverflowError 异常
- 如果在创建新的线程时,没有足够的内存去创建对应的虚拟机栈,会抛出 OutOfMemoryError 异常
静态方法不能使用 this,而非静态方法中可以使用 this?
如果当前帧(虚拟机栈最上方的帧,即当前执行方法的栈帧)是由构造方法或者实例方法所产生的,那么该对象的 this 引用会被放在局部变量表中 index 为 0 的地方,因此在示例方法和构造方法中可以使用 this 关键字,而在静态方法的局部变量表中没有存储 this 的引用,因此无法使用 this 关键字
可以在 IDEA 中安装 jclasslib
插件,通过该插件来查看文件的字节码:
public class Test {
public void testThis() {
int b = 100;
System.out.println("testThis");
}
public static void testNoThis() {
int a = 10;
System.out.println("testNoThis");
}
}
对于上述生成的字节码文件,我们可以看到 testThis
方法的局部变量表中有 this 变量,而 testNoThis 方法的局部变量表中没有 this 变量,如下图:
本地方法栈
本地方法栈是虚拟机所使用到的 本地方法
,比如 C++ 方法
一个 Native 方法就是一个 Java 调用非 Java 代码的接口
为什么需要本地方法?
Java是一门高级语言,我们不直接与操作系统资源、系统硬件打交道。如果想要直接与操作系统与硬件打交道,就需要使用到本地方法了
底层就是这么实现的,在多线程部分就会有很多 Java 调用 Native 方法的示例