JDK7将String常量池从Perm区移动到了Java Heap区。在JDK1.6中,intern方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中的实例。但是在JDK1.7以后,String.intern()方法不会在复制实例,只是在常量池中记录首次出现的实例引用。下面来看一些具体例子。

案例一:

        String str1 = new String("计算机") + new String("软件");
System.out.println(str1.intern() == str1); String str2 = new String("ja") + new String("va");
System.out.println(str2.intern() == str2);

输出结果:

JDK1.6  false  false
JDK1.7  true  false

分析:在JDK1.6中,intern方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中的实例,而由new String()创建的字符串实例在Java堆中所以不是同一个引用,都返回false。但是在JDK1.7以后,String.intern()方法不会在复制实例,只是在常量池中记录首次出现的实例引用,因此str1.intern()=str1,但是Java字符串是Java中的关键字,早已创建,所以str2.intern()!=str2。当然如果用StringBuilder创建字符串效果也是一样的。

        String str1=new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern()==str1); String str2=new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern()==str2);

接下来再给两个特殊例子:

代码一:

public static void main(String[] args) {
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);
}

输出结果:

jdk6下  false false

jdk7下  false true

代码二:

public static void main(String[] args) {
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);
}

输出结果:

jdk6下  false false

jdk7下  false false

分析:

首先,jdk6中的解释

注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向。

  如上图所示。首先说一下 jdk6中的情况,在 jdk6中上述的所有打印都是 false 的,因为 jdk6中的常量池是放在 Perm 区中的,Perm 区和正常的 JAVA Heap 区域是完全分开的。上面说过如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而 new 出来的 String 对象是放在 JAVA Heap 区域。所以拿一个 JAVA Heap 区域的对象地址和字符串常量池的对象地址进行比较肯定是不相同的,即使调用String.intern方法也是没有任何关系的。

  再说说 jdk7 中的情况。这里要明确一点的是,在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的 Perm 区的,Perm 区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError: PermGen space错误的。 所以在 jdk7 的版本中,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了。为什么要移动,Perm 区域太小是一个主要原因,当然据消息称 jdk8 已经直接取消了 Perm 区域,而新建立了一个元区域。应该是 jdk 开发者认为 Perm 区域已经不适合现在 JAVA 的发展了。

正是因为字符串常量池移动到 JAVA Heap 区域后,再来解释为什么会有上述的打印结果。

  • 在代码一中,先看 s3和s4字符串。String s3 = new String("1") + new String("1");,这句代码中现在生成了2个对象,一个是字符串常量池中的“1” 和 另一个是JAVA Heap 中的 s3引用指向的对象。中间还有2个匿名的new String("1")我们不去讨论它们。此时s3引用对象内容是"11",但此时常量池中是没有 “11”对象的。
  • 接下来s3.intern();这一句代码,是将 s3中的“11”字符串放入 String 常量池中,因为此时常量池中不存在“11”字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个 "11" 的对象,关键点是 jdk7 中常量池不在 Perm 区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的。
  • 最后String s4 = "11"; 这句代码中"11"是显示声明的,因此会直接去常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向 s3 引用对象的一个引用。所以 s4 引用就指向和 s3 一样了。因此最后的比较 s3 == s4 是 true。

  • 再看 s 和 s2 对象。 String s = new String("1"); 第一句代码,生成了2个对象。常量池中的“1” 和 JAVA Heap 中的字符串对象。s.intern(); 这一句是 s 对象去常量池中寻找后发现 “1” 已经在常量池里了。

  • 接下来String s2 = "1"; 这句代码是生成一个 s2的引用指向常量池中的“1”对象。 结果就是 s 和 s2 的引用地址明显不同。图中画的很清晰。

  • 来看代码二代码,从上边第二幅图中观察。第一段代码和第二段代码的改变就是 s3.intern(); 的顺序是放在String s4 = "11";后了。这样,首先执行String s4 = "11";声明 s4 的时候常量池中是不存在“11”对象的,执行完毕后,“11“对象是 s4 声明产生的新对象。然后再执行s3.intern();时,常量池中“11”对象已经存在了,因此 s3 和 s4 的引用是不同的。
  • 第二段代码中的 s 和 s2 代码中,s.intern();,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s = new String("1");的时候已经生成“1”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s 和 s2 的引用地址是不会相等的。

接下来再看一个例子:

        String str1=new StringBuffer("计算机").append("软件").toString();
str1.intern();
String str2="计算机软件";
System.out.println(str1==str2);

输出结果:

JDK1.6 false

JDK1.7 true

分析:StringBuffer创建字符串和new String()创建字符串类似,分析过程如上。同样,如果str1.intern();和String str2="计算机";互换位置,则上述输出结果均为false。

        String str1=new StringBuffer("计算机").toString();
str1.intern();
String str2="计算机";
System.out.println(str1==str2);

输出结果:

JDK1.6 false

JDK1.7 false

总结:

  • JDK7将String常量池从Perm区移动到了Java Heap区
  • 调用String.intern()方法时,会在常量池中直接保存首次创建该对象的引用,而不会重新创建对象
  • 如果创建的字符串是关键字,则常量池中保存的是关键字的引用,而不是创建该关键字时的对象引用。
  • 如果字符串不是拼接而成,则在创建该字符串时就已经把对应字符串放进常量池中了。所以要想使str.intern=str,则此字符串必须是拼接而成(可以通过String或者StringBuffer对象创建)。

常量池之String.intern()方法的更多相关文章

  1. 字符串常量池和String.intern()方法在jdk1.6、1.7、1.8中的变化

    字符串常量池也是运行时常量池 jdk1.6中,它是在方法区中,属于“永久代” jdk1.7中,它被移除方法区,放在java堆中 jdk1.8中,取消了“永久代”,将常量池放在元空间,与堆独立了 pub ...

  2. 关于jvm中的常量池和String.intern()理解

    1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. ne ...

  3. Knowledge Point 20180309 字符串常量池与String,intern()

    引言 什么都先不说,先看下面这个引入的例子: public static void test4(){ String str1 = new String("SEU") + new S ...

  4. 对于JVM中方法区,永久代,元空间以及字符串常量池的迁移和string.intern方法

    在Java虚拟机(以下简称JVM)中,类包含其对应的元数据,比如类的层级信息,方法数据和方法信息(如字节码,栈和变量大小),运行时常量池,已确定的符号引用和虚方法表. 在过去(当自定义类加载器使用不普 ...

  5. String放入运行时常量池的时机与String.intern()方法解惑

    运行时常量池概述 Java运行时常量池中主要存放两大类常量:字面量和符号引用.字面量比较接近于Java语言层面的常量概念,如文本字符串.声明为final的常量值等. 而符号引用则属于编译原理方面的概念 ...

  6. JVM体系结构之七:持久代、元空间(Metaspace) 常量池==了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap

    一.intern()定义及使用 相信绝大多数的人不会去用String类的intern方法,打开String类的源码发现这是一个本地方法,定义如下: public native String inter ...

  7. 基本数据类型的常量池与String类型常量池解析

    抛出样例: Integer a1  = new Integer(123);        Integer a2  = new Integer(123);        System.out.print ...

  8. String intern 方法 jdk中的描述

    一个初始为空的字符串池,它由类 String 私有地维护. 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中 ...

  9. 浅析String.intern()方法

    1.String类型“==”比较样例代码如下:package com.luna.test;public class StringTest { public static void main(Strin ...

随机推荐

  1. Python 可迭代的对象、迭代器和生成器

    迭代是数据处理的基石.扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项.这就是迭代器模式(Iterator pattern). p.p1 { margin: 0 ...

  2. JS中的函数、BOM和DOM操作

     一.JS中的函数 [关于注释] /** [文档注释]:开头两个*.写在函数上方,在调用函数时可以看到文档上方的描述信息. */   // 单行注释 /* 多行注释 */ 1.函数的声明及调用 (1) ...

  3. Android开发 去掉标题栏方法 摘记

    1.用代码里实现 提示:这句代码要写在Activity中的setContentView()方法前面. 2.在清单文件(Androidmanifest.xml)里面实现   android:theme= ...

  4. "R6002 floating point support not loaded"问题分析

    今天为了追踪程序线程退出耗时时间,添加了耗时时间输出日志: TimerMeter tm; // do some threads exit logic float fSecs = tm.Elapsed( ...

  5. Oracle日期查询:季度、月份、星期等时间信息

    --oracle有关时间信息 Select to_char(sysdate,'Q') from dual;--指定日期的季度 Select to_char(sysdate,'MM') from dua ...

  6. Java入门(5)——类和对象还有构造方法

    类     类和对象的概念: 类是对一群具有相同属性.行为的事物的统称.        类是抽象的.       人以类聚 物以群分 对象:      对象是现实生活中的1个具体存在.      看得 ...

  7. sort与qsort的用法,建议使用sort

    做ACM题的时候,排序是一种经常要用到的操作.如果每次都自己写个冒泡之类的O(n^2)排序,不但程序容易超时,而且浪费宝贵的比赛时间,还很有可能写错.STL里面有个sort函数,可以直接对数组排序,复 ...

  8. python基础教程(一)

    之所以选择py交易有以下几点:1.python是胶水语言(跨平台),2.python无所不能(除了底层),3.python编写方便(notepad++等文本编辑器就能搞事情),4.渗透方面很多脚本都是 ...

  9. python 数据驱动(ddt)

    DDT包含类的装饰器ddt和两个方法装饰器data(直接输入测试数据),file_data(可以从json或者yaml中获取测试数据) 实例代码: import ddt import unittest ...

  10. jmeter系列-------注意事项

    1.自己创建的数据自己擅长,不要留垃圾数据 2.每个接口都需要增加断言,保证脚本的结果的正确性 3.相同的应用放在一个简单控制器下,所有的应用尽量放在一个线程组下面,将特殊场景单独抽离成一个线程组 4 ...