浅谈jvm运行时常量池(基于jdk7)

前言

在阅读文章之前,先来做一道题:下面代码会输出什么呢?

$ String a  ="abd";
$ String b = "abc";
$ System.out.println(a==b);

介绍

在上一篇文章中晚饭简单讲解了JVM是如何加载class文件的,浅解JVM加载class文件 | Mrsssswan.club
内存的控制权在于JVM,JVM在执行java程序时会把它管理的内存划分为不同的数据区

浅谈jvm运行时常量池(基于jdk7)
该图来源于JVM入门——运行时数据区 - OKevin - 博客园

静态常量池

加载class文件之后内存中会生成该类的对象,并把该对象实例放在堆中

堆中的对象是可以共享的,方法区也和堆一样,它用于存储已被JVM加载了的数据。这其中就有常量,静态变量。

运行时常量池是方法区的一部分。
在上面题目中,“abc”这个字符串是放在常量池中的,内存中只有一份,是“共享”的,所以a,b指向的是同一个,答案为true

隐隐预约懂了一丢丢?那再做个题
下面一共创建了几个对象呢??

$ String a  ="a"+"b"+"c";

赋值右边的“a”,“b”,“c”其实都是常量,编译时会进行优化,将它们的连接结果存储起来
浅谈jvm运行时常量池(基于jdk7)

当JVM执行到String a =“a”+“b”+"c"时,编译器会进行优化,相当于执行的是String a =“abc”,这时候就会在常量池中找,如果没有这个字符串,就会产生一个对象

所以,答案是只创建了一个对象

常量池的动态性

前面晚饭讲的是静态常量池,而运行期间也是可以放入新的常量的,称之为运行时常量池。可以使用String类的intern()方法来实现

调用intern()方法中,如果常量池中已经包含String对象的字符串,返回该字符串,否则将String对象的字符串添加进池中,并返回其引用

$ String s1 = "abc";
$ String s2 = new String("abd");
$ s2 = s2.intern();
$ System.out.println(s1==s2);

输出为 true
一定得说清楚的一点就是,new出来的对象是在堆中分配的,所以下面这两条语句其实在不同内存区域中存放了不同的数据

$ String s1 = new String("abd");   //heap中创建一个String的实例
$ s1 = s1.intern();                    //常量池中存放abc这个字符串

一张图可能更清晰的说明

浅谈jvm运行时常量池(基于jdk7)
来源于String的final和String常量池 - SGY123 - 博客园

补充编译时变量

在前面讲到

$ String a  ="abd";
$ String b = "abc";
$ System.out.println(a==b);

会输出true,因为“abc”是常量,但如果改成这样结果还是会为true吗

$ String a ="a";
$ String b ="b";
$ String c ="c";
$ String s = a+b+c; 
$ String s1 = "abc";
$ System.out.println(s==s1); //true还是false???

a,b,c三个变量,都是不可预料的,编译器是编译时不能确定它的值是多少,只会在执行时创建新的String对象存储到堆中

浅谈jvm运行时常量池(基于jdk7)
晚饭自己画的图嘻嘻嘻~~~

所以,答案为false

总结

1.常量池中存储了编译器编译后的数据,而运行时常量池的常量,基本来源于各个class文件中的常量池,可以理解为方法区的“共享”.
2.程序运行期间,可以手动想常量池添加常量,例如调用String的intern()方法。
3.要注意区分new出来的对象是在堆中创建的
4.编译器会对常量进行优化,但编译期间无法确定变量的值

浅谈jvm运行时常量池就到这里了啊,有什么疑问可以发晚饭邮箱!~

晚饭后来看到一篇美团技术的文章,讲解的更清楚,看完文章后更是自愧不如,对比之下文章很不严谨
大家可以看下美图的这篇文章深入解析String#intern


晚饭邮箱:1520010400@qq.com


公众号

欢迎关注,微信公众号,获取最新文章

浅谈jvm运行时常量池(基于jdk7)

上一篇:「NOIP2021模拟赛8.23 C」棒棒糖女孩(lollipop)题解


下一篇:BestCoder17 1002.Select(hdu 5101) 解题报告