整体结构
内容解释
JVM包含有:堆,方法区,程序计数器,线程栈,本地方法栈
前两者是线程共享的,后三者是线程私有
堆
被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。
对可以按照可扩展来实现(通过-Xmx 和-Xms 来控制)
当队中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。
老年代:2/3的堆空间
年轻代:1/3的堆空间
eden区:8/10 的年轻代
survivor0: 1/10 的年轻代
survivor1:1/10的年轻代
方法区
被所有方法线程共享的一块内存区域。
用于存储已经被虚拟机加载的类信息,常量,静态变量等。
这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。
程序计数器
是当前线程锁执行字节码的行号治时期,每条线程都有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果是Natice方法,则为空。
线程栈
每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。
每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
通常所说的栈,一般是指在虚拟机栈中的局部变量部分。
局部变量所需内存在编译期间完成分配,
如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。
如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。
局部变量表
保存当前栈帧中的临时变量,包含各种数据类型、对象的引用以及方法返回的地址
操作数栈
操作数栈是一个后入先出栈,说白了也就是一个在对局部变量进行操作的时候一个中间缓存栈
作用:在方法执行过程中,写入(进栈)和提取(出栈)各种字节码指令
分配时期:同上,在编译时会在方法的 Code 属性的 max_stacks 数据项中确定操作数栈的最大深度
栈容量:操作数栈的每一个元素可以是任意的 Java 数据类型 ——32 位数据类型所占的栈容量为 1,64 位数据类型所占的栈容量为 2
注意:操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,在编译时编译器需要验证一次、在类校验阶段的数据流分析中还要再次验证
动态链接
定义:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接
静态解析和动态连接区别:
Class 文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数,这些符号引用:
一部分会在类加载阶段或者第一次使用的时候就转化为直接引用(静态解析)
另一部分会在每一次运行期间转化为直接引用(动态连接)
方法返回
方法退出的两种方式:
正常退出:执行中遇到任意一个方法返回的字节码指令
异常退出:执行中遇到异常、且在本方法的异常表中没有搜索到匹配的异常处理器区处理
作用:在方法返回时都可能在栈帧中保存一些信息,用于恢复上层方法调用者的执行状态
正常退出时,调用者的 PC 计数器的值可以作为返回地址
异常退出时,通过异常处理器表来确定返回地址
方法退出的执行操作:
恢复上层方法的局部变量表和操作数栈
若有返回值把它压入调用者栈帧的操作数栈中
调整 PC 计数器的值以指向方法调用指令后面的一条指令等
在实际开发中,一般会把动态连接、方法返回地址与其他附加信息全部一起称为栈帧信息
本地方法区
和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。也会抛出StackOverflowError 和OutOfMemoryError。每个线程只会加载自己所依赖的本地方法,也是线程私有的区域
------------END-----------
领取专属 10元无门槛券
私享最新 技术干货