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. js 日期大小比较

    <!DOCTYPE HTML><html><body><script>//获取起始日期 //转换为日期格式var startDate='2016-06- ...

  2. RHCE认证过程

    RHCE认证,即红帽认证工程师(Red Hat Certified Engineer)的简称,认证内容包括DNS.NFS.Samba.Sendmail.Postfix.Apache和关键安全功能的详细 ...

  3. canvas自适应圆形时钟绘制

    前面的话 前面介绍过canvas粒子时钟的绘制,本文将详细介绍canvas自适应圆形时钟绘制 效果演示 最终自适应圆形时钟的效果如下所示 功能分析 下面来分析一下该圆形时钟的功能 [1]静态背景 对于 ...

  4. EXE转JPG后缀格式工具(真实JPG后缀)

    EXE转JPG后缀格式工具(真实JPG后缀) exe转换为jpg工具,双击之后正常运行. 杀毒软件误报 本程序禁止用于违法操作. 下载地址 链接: https://pan.baidu.com/s/1b ...

  5. hdu3720 Arranging Your Team

    Arranging Your Team Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  6. 大手册(书籍)排版利器-XML自动排版生成工具

    --支持全球化/多语言/符合W3C标准的XML自动排版工具 Boxth XML/XSL Formatter是专为XML数据或其他结构化数据源自动输出排版文件(如: PDF等)而设计的集数据格式化.版式 ...

  7. python基础教程(四)

    列表 本节继续讨论列表不同元组和字符串的地方:列表是可变的(mutable)----可以改变列表的内容,并且列表有很多有用的.专门的方法. List函数可以将一个字符串拆分成列表. >>& ...

  8. django框架简介

    -------------------MVC与MVT框架-------------------1.MVC MVC框架的核心思想是:解耦.降低各功能模块之间的耦合性,方便将来变化时,更容易重构代码,最大 ...

  9. 关于vue的一些总结

    最近学习了一段时间的vue js   除了路由没用过之外  基本上其他的都用过了 对于vue的一些用法  在此做一个总结.  开始使用vue之前 需要导入vuejs这个文件到项目中 <scrip ...

  10. EL表达式的js函数传参问题

    <!Doctype html> <html> <head> <title>js的传参问题</title> <script type=& ...