JVM虚拟机定义了若干程序运行期间会使用的运行时数据区,其中有一些会随着虚拟机的启动而启动,随着虚拟机的退出而销毁,另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程的开始和结束而创建和销毁。
前面我们介绍过,运行时数据区中包含线程共有的方法区和堆空间,以及线程私有的本地方法栈,虚拟机栈以及程序计数器,下面我们一一介绍这些区域。
程序计数器 又叫做PC寄存器
- 线程私有的,生命周期和线程的生命周期保持一致。
- pc寄存器是用来存储执行下一条指令的地址,也即将要执行的代码。由执行引擎读取下一条指令。
使用PC寄存器存储字节码指令地址有什么用?
答:因为CPU需要不断的切换线程,这时候切换回来以后需要知道接着从哪个位置继续执行。 也就是起到了保存现场的作用
pc寄存器为什末会被设定为线程私有的
答: 我们都知道所谓的多线程在一个特定的时间段内只会执行其中某一段线程的方法,CPU会不断的切换任务执行。这样就必然会导致经常中断或恢复,如何保证分毫不差?为了保证准确的记录各个线程执行的当前字节码指令地址,最好的办法是为每个线程都分配一个pc寄存器,这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。 - 程序计数器是唯一一个不会发生OOM的地方
虚拟机栈
- 每个线程创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应着一次次的JAVA方法调用。
- 线程私有。
- 生命周期:生命周期和线程一致。
- 作用是:主管java程序的运行,它保存方法的局部变量(8中基本数据类型,对象的引用地址),部分结果,并参与方法的调用和返回。
栈的特点:
- 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。
- jvm直接对JAVA栈的操作只有两个: 每个方法执行伴随着进栈(入栈,压栈) 执行结束之后的出战操作
- 对于栈来说不存在垃圾回收问题
栈中经常出现的异常就是栈溢出异常,一般出现在递归中 ,没有设置递归结束条件,或者递归的层数太深 所占的空间超过栈的默认最大空间。 StackOverflow
设置栈内存大小
我们可以通过-Xss 设置栈的空间大小
-Xss1024k
-Xss1m
栈的内部结构
每个栈帧里存储着
-
局部变量表(local variables)
-
方法嵌套调用的次数由栈的大小决定,一般来说,栈越大,方法嵌套调用的次数越多。对于一个函数而言,它的参数和局部变量越多,使得局部变量表膨胀,它的栈帧就越大,以满足方法调用所需传递的信息增大的需求,进而函数调用就会占用更多的栈空间,导致其嵌套调用次数就会减少。
-
局部变量表的变量只在当前方法调用中有效。在方法执行的时候虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。当方法调用结束后,随着方法栈帧的销毁,局部变量表也会随之销毁。
-
为什么静态方法里面不能引用this,因为静态方法里面没有存储this变量
非静态方法里面都存储了this -
变量的类型:
- 按照数据类型分类 1. 基本数据类型 2. 引用数据类型。
- 按照在类中声明的位置分
- 成员变量 在使用前都经历过默认初始化赋值
类变量: linking的prepare阶段,给类变量默认赋值 —> initial阶段
实例变量:随着对象的创建,会在堆空间中分配实例变量空间,并进行默认赋值 - 局部变量 在使用前 必须进行显示的赋值,否则编译不通过。
- 成员变量 在使用前都经历过默认初始化赋值
-
-
操作数栈
每个独立的栈帧中除了包含局部变量表以外,还包含一个后进先出的操作数栈,也可以称之为表达式栈。
操作数栈,在方法执行过程中,根据字节码指令往栈中写入数据或提取数据,即入栈或者出栈。 -
动态链接
-
方法返回地址
-
一些附加信息