虚拟机运行时常量池与String的intern方法

来源:互联网 发布:5c5c5c最新域名升级 编辑:程序博客网 时间:2024/06/01 08:20
运行时常量池是方法区的一部分,存放编译期生成的字面量和符号引用,String的intern()方法在运行期间将新常量放入运行时常量池中

在程序的编译期,生成了class文件,代码中的类 ,方法,接口等的常量和字符串常量就放到class文件的常量池中。

在虚拟机加载class文加后,class文件中的常量池就被放到了运行时常量池,这个运行时常量池时方法区的一部分。

运行时常量池有大小限制,一般为4M,如果超出这个限制可能就要抛出OOM异常,往常量池中添加数据,类的加载是一种方式,还有一种用的比较多的方式是String的intern()方法。
简单来说,intern方法是拿字符串到常量池中比较,如果运行时常量池中有该字符串,则返回其引用,若没有,则将该字符串添加到运行时常量池中。
intern方法在文档中的解释:
Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to thisString object as determined by the equals(Object) (euqal方法比较String的内容是否相同,而不比较引用是否相同)method, then the string from the pool is returned. Otherwise, thisString object is added to the pool and a reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() istrue if and only if s.equals(t) is true.


All literal strings and string-valued constant expressions are interned. String literals are defined in §3.10.5 of theJava
举一些面试中常见的例子

public class Intern {

    public static void main(String[] args) {
         System.out.println(System.getProperty("java.vm.version")); //打印java虚拟机版本
         String s = new String("1")+“1”; //用这种方式初始化s是为了不在类加载阶段就将11放到常量池中,生成的s存放在heap中
         s.intern();
         String s2 = "11";
         System.out.println(s == s2);
}

}

发现在不同版本的jdk上编译运行会输出不同的结果
在1.6:


在1.7:

出现两种不同结果;常量池在VM的方法区,方法区在hotspot虚拟机上一版用永久代来实现,因此也是有MaxPermSize上限的,是在jdk1.7后,为了防止常量池溢出,将常量池移到了heap区,也就是实现对象内存分配的堆内存。
如此一来,当执行s.intern()时,常量池所在区域与s对象所在区域都是heap,于是常量池保存了该字符串对象的引用s到常量池。
当生成s2时,由于常量池中已经有11字符串,直接将该引用返回,于是s2指向常量池s,也就是堆内存中s的对象,所以用==比较s与s2的地址时,返回了true。
1 0