前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >常量池之字符串常量池String.intern()

常量池之字符串常量池String.intern()

作者头像
java404
发布2018-05-18 11:28:56
1.1K0
发布2018-05-18 11:28:56
举报
文章被收录于专栏:java 成神之路java 成神之路

运行时常量池是方法区(PermGen)的一部分。

需要提前了解:

1. JVM内存模型

2. JAVA对象在JVM中内存分配

常量池的好处

常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

字符串进入到常量池的两种方法:

1. new String()的实例调用intern()方法。

    执行intern()方法时,若常量池中不存在等值的字符串,JVM就会在常量池中 创建一个等值的字符串,然后返回该字符串的引用。

2. “”(引号)引起来的内容(字面量)。

    引号引起来的字符串,首先从常量池中查找是否存在此字符串,如果不存在则在常量池中添加此字符串对象,然后引用此字符串对象。如果存在,则直接引用此字符串。

重要提示:虚拟机启动时常量池中就存在“java”字符串实例,下面代码中s2调用intern()方法时,只是返回常量池中“java”实例的引用,而没有添加“java”实例。

先看下,下面的代码

代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        String s1 = new StringBuilder().append("aa").append("bb").toString();
        System.out.println(s1.intern() == s1);
        String s2 = new StringBuilder().append("ja").append("va").toString();
        System.out.println(s2.intern() == s2); 
    }
}

输出结果如下:

JDK6

false false

JDK7、JDK8

true false

为什么不同版本的JDK输出的结果还不一样呢?带着疑问我们分别分析下JDK1.6 和JDK1.7-JDK1.8的常量池内存模型。

JDK1.6 常量池内存模型

Paste_Image.png

如图中,我们可以看到常量池位于方法区(PermGen),常量池中引用的字符串实例也在常量池中。

  1. 通过调用intern()方法,会在常量池中生成一个相同字符串的对象
  2. “”内的字符串都会添加到常量池中,相当于引用的方法区中的字符串对象。

根据上图内存模型然后我们在分析下代码:

String s1 = new StringBuilder().append("aa").append("bb").toString();

    System.out.println(s1.intern() == s1);

    s1生成的对象在堆中,而s1.intern()的对象在常量池中,所以返回false。

String s2 = new StringBuilder().append("ja").append("va").toString();

    System.out.println(s2.intern() == s2);

    s2生成的对象在堆中,而s2.intern()的对象也肯定在常量池中,所以也返回false。

JDK1.7-JDK1.8常量池内存模型

Paste_Image.png

如图中,我们可以看到常量池位于方法区(PermGen),但常量池引用的字符串实例在中。(区别在这)

  1. 通过调用intern()方法,会在常量池添加一个此字符串实例的引用,(前提:常量池中没有相同内容的字符串)。
  2. “”内的字符串实例引用会添加到常量池中(前提:常量池中没有相同内容的字符串),如果常量池中存在,则引用常量池中的对象(防止重复创建对象)。

根据上图内存模型然后我们在分析下代码:

    String s1 = new StringBuilder().append("aa").append("bb").toString();

    System.out.println(s1.intern() == s1);

    s1生成的对象在堆中,此时常量池中没有跟s1内容相同的字符串,所以在调用intern方法时,会在常量池中添加此对象的引用,所以返回为true。

String s2 = new StringBuilder().append("ja").append("va").toString();

    System.out.println(s2.intern() == s2);

    s2生成的对象在堆中,而此时常量池中已经有一个跟s2内容相同的字符串常量,当s2调用intern方法时,返回常量池中已经存在的实例(相当于堆中有两个相同内容的实例:一个是new 出来的,一个是常量池中的)所以返回的结果为false。

“java”字符串实例为什么会在虚拟机启动时就存在?

请看下面的例子

代码语言:javascript
复制
 public void meme(){
    String s = new StringBuilder().append("me").append("me").toString();
    System.out.println(s.intern()==s);   //false
}

执行结果为false,说明“meme”也已经存在常量池中了。

    常量池定义:用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

    当类加载到内存后会在常量池中添加类、方法等符号的引用。方法名meme就会以字符串的方式加入到字符串常量池中,所以会感觉常量池中会内置很多字符串常量。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.04.24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 常量池的好处
  • 字符串进入到常量池的两种方法:
  • JDK1.6 常量池内存模型
  • JDK1.7-JDK1.8常量池内存模型
    • “java”字符串实例为什么会在虚拟机启动时就存在?
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档