java8内存模型
一、Java虚拟机运行时数据区
1.规范:

-
虚拟机栈:线程私有,随线程创建而创建。栈里面是一个一个“栈帧”,每个栈帧对应一次方法调用。栈帧中存放了局部变量表(基本数据类型变量和对象引用)、操作数栈、方法出口等信息。当栈调用深度大于JVM所允许的范围,会抛出StackOverflowError的错误。
-
本地方法栈:线程私有,这部分主要与虚拟机用到的Native方法相关,一般情况下,并不需要关心这部分的内容。
-
程序计数器:也叫PC寄存器,JVM支持多个线程同时运行,每个线程都有自己的程序计数器。倘若当前执行的是 JVM 的方法,则该寄存器中保存当前执行指令的地址;倘若执行的是native方法,则PC寄存器中为空。(PS:线程执行过程中并不都是一口气执行完,有可能在一个CPU时钟周期内没有执行完,由于时间片用完了,所以不得不暂停执行,当下一次获得CPU资源时,通过程序计数器就知道该从什么地方开始
-
方法区:方法区存放类的信息(包括类的字节码,类的结构)、常量、静态变量等。字符串常量池就是在方法区中。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的是与Java堆区分开来。无论是永久代还是元空间,都是方法区的实现。
-
堆:堆中存放的是数组(PS:数组也是对象)和对象。当申请不到空间时会抛出OutOfMemoryError。
2.元空间(metaspace)
在JDK1.8中,永久代已经不存在,存储的类信息、编译后的代码数据等已经移动到了MetaSpace(元空间)中,元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory)。常量池和静态变量存储到堆内存中。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。
不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
元空间的大小仅受本地内存限制,可以通过以下参数来指定元空间大小:
- -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值
- +-XX:MaxMetaspaceSize,最大空间,默认是没有限制的
3.String字符串的存储空间
点击查看代码
String full_name = "DUT";
String concat_name = "DU"+"T";
String use_variable_name = full_name + "";
String new_name = new String("DUT");
(1) full_name存储在常量池
String full_name = "DUT";
(2) concat_name存储在常量池
字符串拼接会在编译器进行优化,直接拼接最后结果
String concat_name = "DU"+"T";
(3) use_variable_name存储在堆空间中
由于变量参与了运算,JVM会在堆中开辟新的空间添加字符串
String use_variable_name = full_name + "";
(4)JVM会在堆内存中开辟新的空间,存储字符串常量
String new_name = new String("DUT");
(5) string 有长度限制
2^16 -1 = 65535
但是由于JVM需要1个字节表示结束指令,所以这个范围就为65534了
超出这个范围在编译时期是会报错的,但是运行时拼接或者赋值的话范围是在整形的最大范围。
4.Integer
点击查看代码
public static void main(String[] args) {
Integer i = 10;
Integer j = 10;
System.out.println(i == j); //true
Integer a = 128;
Integer b = 128;
System.out.println(a == b); //false
}
若果声明的是int类型的,则放入常量池
若声明Integer类型的,[-128, 127]范围内,则放入常量池,否则,在堆内存中分配空间存储

浙公网安备 33010602011771号