总结黑马程序员笔记
Java运行时数据区域
可以分成线程私有的和线程共享的区域。
线程私有的区域有:虚拟机栈,本地方法栈,程序计数器
线程共享的区域有:堆,方法区(在JDK1.8中,方法区放在了本地内存中,其实现为元空间),直接内存(不是运行时数据区)
程序计数器的作用:控制程序指令的进行,实现分支,跳转异常等逻辑;另一个作用是记录下一行字节码指定的地址,使得在多线程切换的时候能够找到正确的执行位置。
程序计数器会出现内存溢出吗?
内存溢出是指程序在使用某一块内存时,存放的数据内存大小超过了虚拟机所能提供的上限。
程序计数器是一个用于记录线程执行位置的寄存器,它通常不会出现内存溢出的情况。因为程序计数器的大小是固定的,通常为32位或64位,所以不会随着程序的执行而增长。因此,程序计数器不会出现内存溢出的情况。
Java虚拟机栈帧存放哪些内容?
Java虚拟机栈帧中存放的是局部变量表,操作数栈,帧数据。局部变量表的作用是存放局部变量;操作数栈存放临时数据;帧数据主要包含动态链接,方法出口,异常表的引用。
局部变量表
是一个数组,数组中的每个位置称为槽,long和double型变量占两个槽,其他类型占用一个槽,在方法的执行过程中存放所有的局部变量存放在这些槽中,包括方法参数,实例方法的this对象,方法体中声明的局部变量。同时为了节省空间,局部变量表中的槽可以复用,当某一局部变量失效,当前槽可以被再次使用。
操作数栈
用于存放方法执行过程的中间计算结果。
帧数据
包括动态链接,方法出口,异常表的引用。
动态链接:当前类的字节码指令引用了其他类的属性或者方法时,需要将符号引用(编号)转换成对应的运行时常量池中的内存地址。动态链接就保存了编号到运行时常量池的内存地址的映射关系。
方法出口:指的是方法在正确或者异常结束时,当前栈帧会被弹出,同时程序计数器应该指向上一个栈帧中的 下一条指令的地址。所以在当前栈帧中,需要存储此方法出口的地址。
异常表:存放的是代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置。
本地方法栈
Java虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的是native本地方法的栈帧。
在Hotspot虚拟机中,Java虚拟机栈和本地方法栈实现上使用了同一个栈空间。本地方法栈会在栈内存上生成一个栈帧,临时保存方法的参数同时方便出现异常时也把本地方法的栈信息打印出来。
Java堆
几乎所有的对象实例和数组都在堆中分配内存
为什么并不是所有的对象实例及数组在堆中分配内存?
原因在于:Java虚拟机引入了逃逸分析,会判断在方法中的对象是否会被外部方法使用。如果经过逃逸分析发现,方法中的对象不会被外部方法使用,那么这个对象就可能在栈中分配内存了。
public static StringBuffer craeteStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb;
// 该StringBuffer对象sb就返回出去了,可能会被其他方法所使用
}
public static String createStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
//这个StringBuffer对象sb就没有被返回,因此不会被其他方法使用
//返回的是String 对象
}
参考文章:深入理解Java中的逃逸分析_hollischuang 深入分析java的编译原理-CSDN博客
未完结