zoukankan      html  css  js  c++  java
  • 常量池之字符串常量池String.intern()

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

    需要提前了解:
    1. JVM内存模型
    2. JAVA对象在JVM中内存分配

    常量池的好处

    常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
    - Java的自动装箱中其实就使用到了运行时常量池。详见:Java 自动装箱与拆箱的实现原理
    - 还有字符串常量池。

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

    1. new String()的实例调用intern()方法。
        执行intern()方法时,若常量池中不存在等值的字符串,JVM就会在常量池中 创建一个等值的字符串,然后返回该字符串的引用。
    2. “”(引号)引起来的内容(字面量)。
        引号引起来的字符串,首先从常量池中查找是否存在此字符串,如果不存在则在常量池中添加此字符串对象,然后引用此字符串对象。如果存在,则直接引用此字符串。

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

    先看下,下面的代码

    1 public class Test {
    2     public static void main(String[] args) {
    3         String s1 = new StringBuilder().append("aa").append("bb").toString();
    4         System.out.println(s1.intern() == s1);
    5         String s2 = new StringBuilder().append("ja").append("va").toString();
    6         System.out.println(s2.intern() == s2); 
    7     }
    8 }

    输出结果如下:

    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。

  • 相关阅读:
    DSA——基数排序
    cannot instantiate the type
    DSA——从尾到头打印链表笔记
    DSA——AVL平衡二叉树
    Leetcode——438. Find All Anagrams in a String【java】【没搞清楚呢】
    Leetcode——415. Add Strings【java】
    Leetcode——387. First Unique Character in a String【java】
    Leetcode——344. Reverse String
    Leetcode——205-Isomorphic Strings(同构字符串)【待反思周末】
    DSA——Shell排序算法
  • 原文地址:https://www.cnblogs.com/bsjl/p/8623494.html
Copyright © 2011-2022 走看看