运行时常量池概述

Java运行时常量池中主要存放两大类常量:字面量和符号引用。字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值等。

而符号引用则属于编译原理方面的概念,包括了下面三类常量:

- 类和接口的全限定名(包名+类名)

- 字段的名称和描述符

- 方法的名称和描述符

运行时常量池位置

运行时常量池在JDK1.6及之前版本的JVM中是方法区的一部分,而在HotSpot虚拟机中方法区放在了”永久代(Permanent Generation)”。所以运行时常量池也是在永久代的。

但是JDK1.7及之后版本的JVM已经将运行时常量池从方法区中移了出来,在Java 堆(Heap)中开辟了一块区域存放运行时常量池


本文主要解惑String对象(即文本字符串)何时放入常量池,不涉及上述三类符号引用常量和其他非String常量值。而且本文只讨论主流的HotSpot虚拟机。

String何时放入常量池

记住一句话:直接使用双引号声明出来的String对象会直接存储在常量池中。

代码一:

String a = "计算机软件";

分析:因为计算机软件五个字直接使用了双引号声明,故JVM会在运行时常量池中首先查找有没有该字符串,有则直接返回该字符串在常量池中的引用;没有则直接在常量池中创建该字符串,然后返回引用。此时,该句代码已经执行完毕,不会在java Heap(堆)中创建内容相同的字符串。该字符串只在常量池中创建了一个String对象。


代码二:

String a = new String("计算机软件");

分析:该行代码生成了两个String对象(Stack(栈)中的对象引用不在讨论范围内):第一步,因为计算机软件五个字直接使用了双引号声明,故JVM会在运行时常量池中首先查找有没有该字符串,有则进入第二步;没有则直接在常量池中创建该字符串,然后进入第二步。第二步:在常量池中创建了一个String对象之后,由于使用了new,JVM会在Heap(堆)中创建一个内容相同的String对象,然后返回堆中String对象的引用。该行代码分别在常量池和堆中生成了两个内容相同的String对象。


代码三:

String a = "计算机" + "软件";

分析:由于JVM存在编译期优化,对于两个直接双引号声明的String的+操作,JVM在编译期会直接优化为“计算机软件”一个字符串,故该行代码同代码一


代码四:

String b = "计算机";
String a = b + "软件";

分析:由于b是一个String变量,编译期无法确定b的值,故不会优化为一个字符串。即使我们知道b的值,但JVM认为它是个变量,变量的值只能在运行期才能确定,故不会优化。运行期字符串的+连接符相当于new,故该行代码在Heap中创建了一个内容为“计算机软件”的String对象,并返回该对象的引用。至此,该代码执行完毕,因为没有直接双引号声明计算机软件这5个字的字符串,故常量池中不会生成计算机软件这5个字的字符串。但是会有“计算机”和“软件”这两个String对象,因为他们都用双引号声明了。


代码五:

String final b = "计算机";
String a = b + "软件";

分析:该代码与代码四的唯一区别是将b声明为final类型,即为常量。故在编译期JVM能确定b的值,所以对+可以优化为“计算机软件”5个字的字符串。该代码的运行同代码三和代码一


代码六:

String a = new String("计算机") + "软件";

分析:因为有new,该代码也无法编译期优化,故该行代码只是在Heap中生成了“计算机软件”字符串的String对象,在常量池中没有内容相同的对象生成。


String.intern方法

概述

String.intern()是一个Native方法,它的作用是:如果运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此String内容相同的字符串,并返回常量池中创建的字符串的引用。

JDK1.7改变

当常量池中没有该字符串时,JDK7的intern()方法的实现不再是在常量池中创建与此String内容相同的字符串,而改为在常量池中记录Java Heap中首次出现的该字符串的引用,并返回该引用

验证代码:

String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println((str1.intern() == str1));
//JDK1.6:false
//JDK1.7:true

String b = "计算机";
String a = b + "软件";
System.out.println(a.intern() == a);
//JDK1.6:false
//JDK1.7:true    

测试代码

请运行以下的代码看看你分析的结果和真正的运行结果是否一样,JDK1.6和1.7都要跑一遍,如果你都分析对了,那就是理解了。

//一次放开一个多行注释运行
       /*
        String s = new String("1");
        s.intern();
        String s2 = "1";
        System.out.println(s == s2);
        String s3 = new String("1") + new String("1");
        s3.intern();
        String s4 = "11";
        System.out.println(s3 == s4);
        */
       /*
        String s = new String("1");
        String s2 = "1";
        s.intern();
        System.out.println(s == s2);
        String s3 = new String("1") + new String("1");
        String s4 = "11";
        s3.intern();
        System.out.println(s3 == s4);
        */
  /*
 //+连接但编译器不优化
        String s1=new String("xy") + "z";
        String s2=s1.intern();
        System.out.println( s1==s1.intern() );
        System.out.println( s1+" "+s2 );
        System.out.println( s2==s1.intern() );
        */
      /*// 一般情况
        String s1=new String("xyz") ;
        String s2=s1.intern();
        System.out.println( s1==s1.intern() );
        System.out.println( s1+" "+s2 );
        System.out.println( s2==s1.intern() );
        */

       /*//编译器优化
        String s1 = "xy" + "z";
        String s2 = s1.intern();
        System.out.println( s1==s1.intern() );
        System.out.println( s1+" "+s2 );
        System.out.println( s2==s1.intern() ); 

        */

说明:本文有部分内容摘抄了周志明大神的《深入理解Java虚拟机》一书,部分代码参考了网上多个博客的内容,仅用于学习。无意侵犯版权。

参考:

《深入理解Java虚拟机-JVM高级特性与最佳实践》周志明著

JVM常量池和八种基本数据及字符串

深入解析String#intern

Java永久代去哪儿了

最新文章

  1. .NET Core中合并Expression<Func<T,bool>>的正确姿势
  2. paper 124:【转载】无监督特征学习——Unsupervised feature learning and deep learning
  3. 2016 ICPC北京站现场赛总结(再度流水账)
  4. Java线程池的原理及几类线程池的介绍
  5. matlab 矩阵
  6. Arrays.asList的那点事
  7. 10.2 Vue 环境安装
  8. kubernetes in action - Services
  9. C++ 中 const、volatile、mutable的用法
  10. storm配置文件
  11. makefile 变量展开
  12. Thinkphp时间转换与统计的问题
  13. 数学之美——HMM模型(一)介绍
  14. struts2 的 ServletActionContext 和 actionContext,服务器代码测试, redirect 、dispatcher、chain、redirectAction
  15. bzoj 2212 Tree Rotations
  16. 一起做RGB-D SLAM(7) (完结篇)
  17. Tinyos 第三版Make系统
  18. 用Visual studio2012在Windows8上开发内核驱动监视线程创建
  19. 【BZOJ2797】[Poi2012]Squarks 暴力乱搞
  20. 日志rsyslog

热门文章

  1. java模式之模板模式——抽象类
  2. Jenkins配置Gogs webhook插件
  3. [LeetCode] My Calendar I 我的日历之一
  4. [BJOI 2011]元素
  5. [NOIp 2013]货车运输
  6. NOIP 2015运输计划
  7. ●BZOJ 4318 OSU!
  8. hihocoder #1142 : 三分·三分求极值
  9. Linux 基本概念和操作2
  10. Python中的条件和循环语句