内存模型
1、程序计数器:
当前线程正在执行的字节码行号指示器,唯一没有规定OOM的区域;
2、JAVA虚拟机栈:
由一个一个的栈帧组成,每一个方法的执行,对应着一个栈帧在虚拟机栈中入栈、出栈,可以抛出StackOverflowError(栈深度超过虚拟机栈最大深度)、OutOfMemoryError(虚拟机在扩展时无法申请到内存)异常。栈帧的结构包括:
3、局部变量表:
连续的内存空间,存放方法参数和局部变量,还有八大基本类型、对象引用(reference)、returnAddress类型;
3.1 操作数栈:
虚拟机栈用于计算临时数据的存储区;
3.2 动态链接:
将方法区中的符号引用转化为直接引用的过程;
3.3 方法出口:
下一个方法指令的地址;
4、本地方法栈:
和JAVA虚拟机栈几乎一样,区别在于它执行的native方法;
5、堆:
堆是JAVA内存管理中最大的一块,也是内存回收主要发生区域;
堆被所有线程共享,但是线程的工作内存上的内容是线程独享的;
对象和数组都是在堆上分配的,但是不是所有的对象都是在堆上分配的,因为有JIT编译器的优化;
JAVA通过逃逸分析来判断对象是否只在栈上使用,如果是的话,就通过标量替换来实现栈上分配;
6、方法区:
加载的类定义数据、常量区、静态变量、JIT编译后的代码;
HotSpot中的永久代;
存储变量的元数据信息;
线程安全、大小不固定、可以被垃圾回收;
常量池包括:
6.1 Class常量池:
每一个class文件都有一个Class常量池;
Class文件中包含了类的版本、字段、方法和接口等;
常量池里包含了字面量与符号引用;
6.1.1 字面量:
文本字符串、八大基本类型、声明为Final的常量等;
6.1.2 符号引用:
类和方法的全限定名、字段的名称和描述、方法的名称和描述;
6.2 运行时常量池:
运行时常量池就是Class常量池被加载到内存后的版本;
不同之处在于,它的字面量可以动态的添加、符号引用会被解析为直接引用;
6.3 字符串常量池:
存储一个StringTable,是一个HashSet表,这个StringTable的实例只有一个,被所有线程共享;
从1.7开始,被移到堆里了,因为方法区的空间太小了;
正常新建字符串,是在先去常量池中找有没有这个字符串,有的话就直接返回,没有的话,再在常量池中新建一个返回;
使用new的方式一定会在堆里新建字符串,Java1.7之后,使用intern函数,会先去常量池中找有没有,有的话直接返回,如果是在堆中,则将堆中的地址返回给常量池,再返回常量池中的引用;
String str = “JA” + “VA”;
String str = “JAVA”;
编译器会将第一句优化成第二句;
String str2 = str1 + “test”;
编译器会将上面这句优化为使用;
对象访问定位:
1、句柄访问:
Reference中存的是稳定的句柄,在垃圾回收时比较稳定;
2、直接指针访问:
访问速度快,节省了一次指针定位开销;