关于java:今天我们基于jdk8聊聊JVM常量池希望对大家有帮助

34次阅读

共计 3138 个字符,预计需要花费 8 分钟才能阅读完成。

前言

本文次要讲 三种常量池,类文件中常量池,运行时常量池,字符串常量池  各自寄存的中央,以及对代码的影响

类文件中常量池(The Constant Pool)

通过 javac 编译后的 class 文件 记录了这个类的所有信息,其中一个局部被成为常量池,外面寄存编译器生成的 字面量 (Literal) 和符号援用(Symbolic References)

字面量:

1. 文本字符串

2.8 中根本类型的值

3.final 常量等

符号援用:

1. 类和接口的全限定名

2. 字段的名称和形容

3. 办法的名称和形容

 

运行时常量池(The Run-Time Constant Pool)

常量池是在 class 文件中的,当该类被加载,常量池信息就会放入运行时常亮池,并把外面的符号援用改为真是地址,运行时常量池是在办法区(1.8 之后追随办法区一起挪动到元空间), 能够在 JVM 运行期间动静向常量池写入数据

运行时常量的包装类

8 种根本数据类型都有本人的包装类,在包装类对象创立的瞎话就会耗费资源,因而  java 对 其中 5 种(Byte,Short,Integer,Long,Character,Boolean)包装类实现了常量池技术, 默认创立了数值 (-128 ,127) 的相应类型的缓存数据,然而超出了此范畴仍然会去创立新的对象。两种浮点数类型的包装类 (Float,Double) 并没有实现常量池技术

咱们晓得,Integer 是 int 的包装类, 而包装类是对象, 创建对象就须要耗费资源.
java 中的根本类型的包装类根本都实现了常量池技术.

代码演示:

        // 只拿 Integer 举例
        Integer a = 10; // 此处有一步装箱操作 Integer  valueOf
        Integer b = 10;
        System.out.println(a == b);// 实现了常量池技术,所以此处相等
        Integer c = 200;
        Integer d = 200;
        System.out.println(c == d);
 
        Integer e = new Integer(10);
        System.out.println(a == e);// new 了新对象  与 a 所在的常量池 地址不一样
 
        Integer f = new Integer(10);
        f = f+0; // 关键在于此处进行了运算,固 f 在此处进行了主动拆箱操作,拆箱后 是 int 数值
        System.out.println(a == f);

看下 valueOf 源码,默认大小范畴内的值 被放入到了 IntegerCache 中,否则 new 新对象

   public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

字符串常量池

JVM 为了晋升性能和缩小内存开销,防止字符串的反复创立,其保护了一块非凡的内存空间,贮存不反复的字符串.,字符串常量存在于堆中

创立形式

1. 在间接应用双引号 ”” 申明字符串的时候,java 都会去常量池通过 equal 找有没有雷同的字符串,如果有,则将常量池的援用返回给变量,如果没有 回在常量池中创立一个对象,而后返回这个对象的援用

2. 应用 new 关键字创立,例如 String a = new String(“a”), 这里首先会去常量池比照有没有 ”a”, 没有则会创立,其次 new 肯定会在堆外面创立一个新对象 并返回该对象的援用

3. 应用 + 运算符, 此处有大抵有三种状况,

String str = “ab”+”cd”; 在常量池上创立常量 ab, cd ,abcd 返回 abcd【着重理解 abcd 在常量池上】

String str = new String(“ab”) + new String(“cd”);  在堆上创建对象 ab、cd 和 abcd,在常量池上创立常量 ab 和 cd , 常量池上不会创立 abcd【着重理解 abcd 不会在常量池上】

还有混合应用的就不在一一阐明 如 String str = “ab” + new String(“cd”); 或者 String strAb = “ab”; String str = strAb + new String(“cd”) 等状况

当初须要理解一个十分要害的办法 public native String intern();

简略来说就是 intern 用来返回常量池中的某字符串,如果常量池中曾经存在该字符串,则间接返回常量池中该对象的援用。否则,在常量池中退出该对象【JDK1.7 之后不再是把该字符串间接退出常量池, 而是将其地址援用放到常量池】,而后 返回援用

以下代码演示(特地阐明 本人写 demo 时候 无关代码肯定要移除,哪怕一个简略 String str=”ab”; 哪怕不援用也会影响代码的预期后果,并且全副在 jdk8 下调试)

代码 1:验证 字符串与 new

       String s1 = "ab"; //s1 指向 ab 常量池地址
       String s2 = new String("ab"); //s2 指向 堆 外面 ab 的地址
       System.out.println(s1==s2);// 固为 false

代码 2:验证 intern()办法

        String s1 = "ab"; //s1 指向 ab 常量池地址
        String s2 = new String("ab"); //s2 指向 堆 外面 ab 的地址
        String s3 = s2.intern();// s3 指向 "ab" 所在常量池地址
        System.out.println(s1 == s2);// false
        System.out.println(s1 == s3);// true , 因为 s3 指向 ab 所在常量池 与 s1 指向始终  固为 true
        System.out.println(s2 == s3);// 固为 false

代码 3:验证运算符

        String s2 = new String("a") +"b"; //s2 指向 堆 外面 ab 的地址, 并且常量池不存在 ab
        String s3 = s2.intern();// 因为常量池并没有 ab 因而会把 s2 的堆地址援用 放到常量池
        System.out.println(s2 == s3);// 同时指向 ab 堆外面的地址 固为 true

代码 4:验证运算符与字符串混用, 和代码 2 的区别仅仅就是多定义了一个 String s1 =”ab”, 返回后果就截然不同

        String s1 = "ab"; //s1 指向 ab 常量池地址
        String s2 = new String("a") +"b"; //s2 指向 堆 外面 ab 的地址, 常量池已存在 ab
        String s3 = s2.intern();// 因为常量池存在 ab 因为会把 ab 常亮池的援用返回
        System.out.println(s2 == s3);// s2 指向堆 s3 地址字符常量池 固为 false

代码 5:来一个非凡字符串 与代码 3 一样仅仅是字符串替换成了 ”java” 返回后果为 false  其实此处就是因为 jvm 虚拟机在其余类【Version.class】先定义了放入了常量池,其实原理就和代码 4 一样 先把 ab 放入了常量池 理解了原理就能够触类旁通

        String s2 = new String("ja") +"va"; //s2 指向 堆 外面 java 的地址, 常量池已存在 java【起因查看 Version.class】String s3 = s2.intern();// 因为常量池存在 java 因为会把 java 常亮池的援用返回
        System.out.println(s2 == s3);// s2 指向堆 s3 地址字符常量池 固为 false

附上 Version.class 截图

总结

在文章的最初作者为大家整顿了很多材料!包含 java 外围知识点 + 全套架构师学习材料和视频 + 一线大厂面试宝典 + 面试简历模板 + 阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题 +Spring 源码合集 +Java 架构实战电子书等等!
如果有须要的敌人欢送关注公众号:前程有光,支付!

正文完
 0