运行时常量池概述

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永久代去哪儿了

String放入运行时常量池的时机与String.intern()方法解惑的更多相关文章

  1. 运行时常量池中的符号引用/String.intern() /ldc指令

    运行时常量池,之前放在方法区(永久代)中,1.8之后被转移到元空间,放到了native memory中. 具体的数据结构是:(看对象的内存布局,句柄访问还是对象头中保存指向类的元数据的指针,这里以对象 ...

  2. JVM笔记3-java内存区域之运行时常量池

    1.运行时常量池属于线程共享区中的方法区. 2.运行时常量池用于编译期生成的各种自变量,符号引用,这部分内用将在类加载后接入方法区的运行时常量池中存放. 看如下代码所示,如图: public clas ...

  3. Java 运行时常量池

    运行时常量池是方法区的一部分.class中除了有类的版本,字段,方法,接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放在方法区的运行时常量池 ...

  4. JVM-String常量池与运行时常量池

    Start with JVM 周志明先生著-<深入理解Java虚拟机>,书买回来好几天了,但是最近才准备开始搞一搞了(哭瞎…..).首先是第一章的Java以及JVM发展历史,大概知道了现行 ...

  5. JVM 常量池、运行时常量池、字符串常量池

    常量池: 即class文件常量池,是class文件的一部分,用于保存编译时确定的数据. 保存的内容如下图: D:\java\test\out\production\test>javap -ver ...

  6. 彻底搞清楚class常量池、运行时常量池、字符串常量池

    彻底搞清楚class常量池.运行时常量池.字符串常量池 常量池-静态常量池 也叫 class文件常量池,主要存放编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference ...

  7. Class常量池、运行时常量池、字符串常量池的一些思考

    Class常量池.运行时常量池.字符串常量池 class常量池 java代码经过编译之后都成了xxx.class文件,这是java引以为傲的可移植性的基石.class文件中,在CAFEBABE.主次版 ...

  8. JVM详解之:运行时常量池

    目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ...

  9. 类的加载,链接和初始化——1运行时常量池(来自于java虚拟机规范英文版本+本人的翻译和理解)

    加载(loading):通过一个特定的名字,找到类或接口的二进制表示,并通过这个二进制表示创建一个类或接口的过程. 链接:是获取类或接口并把它结合到JVM的运行时状态中,以让类或接口可以被执行 初始化 ...

随机推荐

  1. SpringMVC 自定义类型转换器

    先准备一个JavaBean(Employee) 一个Handler(SpringMVCTest) 一个converters(EmployeeConverter) 要实现的输入一个字符串转换成一个emp ...

  2. 机器学习技法:11 Gradient Boosted Decision Tree

    Roadmap Adaptive Boosted Decision Tree Optimization View of AdaBoost Gradient Boosting Summary of Ag ...

  3. HDU 3094 A tree game

    Problem Description Alice and Bob want to play an interesting game on a tree.Given is a tree on N ve ...

  4. 贼有意思[最长上升公共子序列](SAC大佬测试题)

    题目描述Awson 最近越来越蠢了,一天就只知道 zyys.他定义了一个 zyys 数列:这个数列满足:1.是另外两个数列 A,B 的公共子序列;2.数列单调递增.现在他有一个问题,我们假设知道两个长 ...

  5. [SCOI2008]配对

    题目描述 你有 n 个整数Ai和n 个整数Bi.你需要把它们配对,即每个Ai恰好对应一个Bp[i].要求所有配对的整数差的绝对值之和尽量小,但不允许两个相同的数配对.例如A={5,6,8},B={5, ...

  6. [SDOI2009]HH的项链

    题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH 不断地收集新的贝壳,因此,他的项链变得越来越长. ...

  7. 习题9-8 Uva1632

    题意: 给你n个宝藏,然后给出他们的位置a[i]以及存在时间tim[i],如果能全部拿完,求出最短时间: 否则输出No solution 思路: 对于一段区间[i,j],你取完之后肯定是在最左端或者最 ...

  8. Java Servlet 笔记3

    Servlet 表单数据 很多情况下,需要传递一些信息,从浏览器到 Web 服务器,最终到后台程序.浏览器使用两种方法可将这些信息传递到 Web 服务器,分别为 GET 方法和 POST 方法. 1. ...

  9. C++多态实现原理

    C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数.如果对象类型是派生类,就调用派生类的函数:如果对象类型是基类 ...

  10. gulp填坑记(二)——gulp多张图片自动合成雪碧图

    为优化图片,减少请求会把拿到切好的图标图片,通过ps(或者其他工具)把图片合并到一张图里面,再通过css定位把对于的样式写出来引用的html里面,对于一些图片较多的项目,这个过程可能要花费我们一天的时 ...