java8内存模型

一、Java虚拟机运行时数据区

1.规范:

image

  • 虚拟机栈:线程私有,随线程创建而创建。栈里面是一个一个“栈帧”,每个栈帧对应一次方法调用。栈帧中存放了局部变量表(基本数据类型变量和对象引用)、操作数栈、方法出口等信息。当栈调用深度大于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]范围内,则放入常量池,否则,在堆内存中分配空间存储

posted @ 2021-09-26 15:40  '蜗牛'  阅读(186)  评论(0)    收藏  举报