2

本文主要研究一下java String的intern

String.intern()

java.base/java/lang/String.java

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    //......

    /**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     * @jls 3.10.5 String Literals
     */
    public native String intern();

    //......           

}               
  • 当调用intern方法时,如果常量池已经包含一个equals此String对象的字符串,则返回池中的字符串
  • 当调用intern方法时,如果常量池没有一个equals此String对象的字符串,将此String对象添加到池中,并返回此String对象的引用(即intern方法返回指向heap中的此String对象引用)
  • 所有literal strings及string-valued constant expressions都是interned的

实例

基于jdk12

StringExistInPoolBeforeIntern

public class StringExistInPoolBeforeIntern {

    public static void main(String[] args){
        String stringObject = new String("tomcat");
        //NOTE 在intern之前,string table已经有了tomcat,因而intern返回tomcat,不会指向stringObject
        stringObject.intern();
        String stringLiteral = "tomcat";
        System.out.println(stringObject == stringLiteral); //false
    }
}
  • tomcat这个literal string是interned过的,常量池没有tomcat,因而添加到常量池,常量池有个tomcat;另外由于stringObject是new的,所以heap中也有一个tomcat,而此时它指向heap中的tomcat
  • stringObject.intern()返回的是heap中常量池的tomcat;stringLiteral是tomcat这个literal string,由于常量池已经有该值,因而stringLiteral指向的是heap中常量池的tomcat
  • 此时stringObject指向的是heap中的tomcat,而stringLiteral是heap中常量池的tomcat,因而二者不等,返回false

StringNotExistInPoolBeforeIntern

public class StringNotExistInPoolBeforeIntern {

    public static void main(String[] args){
        String stringObject = new String("tom") + new String("cat");
        //NOTE 在intern之前,string table没有tomcat,因而intern指向stringObject
        stringObject.intern();
        String stringLiteral = "tomcat";
        System.out.println(stringObject == stringLiteral); //true
    }
}
  • tom及cat这两个literal string是interned过的,常量池没有tom及cat,因而添加到常量池,常量池有tom、cat;另外由于stringObject是new出来的,是tom及cat二者concat,因而heap中有一个tomcat
  • stringObject的intern方法执行的时候,由于常量池中没有tomcat,因而添加到常量池,intern()返回的是指向heap中的tomcat的引用;stringLiteral是tomcat这个literal string,由于stringObject.intern()已经将tomcat添加到常量池了并指向heap中的tomcat的引用,所以stringLiteral返回的是指向heap中的tomcat的引用
  • 由于stringLiteral返回的是指向heap中的tomcat的引用,其实就是stringObject,因而二者相等,返回true

javap

基于jdk12

StringExistInPoolBeforeIntern

javac src/main/java/com/example/javac/StringExistInPoolBeforeIntern.java
javap -v src/main/java/com/example/javac/StringExistInPoolBeforeIntern.class

  Last modified 2019年4月6日; size 683 bytes
  MD5 checksum 207635ffd7560f1df24b98607e2ca7db
  Compiled from "StringExistInPoolBeforeIntern.java"
public class com.example.javac.StringExistInPoolBeforeIntern
  minor version: 0
  major version: 56
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #8                          // com/example/javac/StringExistInPoolBeforeIntern
  super_class: #9                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #9.#21         // java/lang/Object."<init>":()V
   #2 = Class              #22            // java/lang/String
   #3 = String             #23            // tomcat
   #4 = Methodref          #2.#24         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = Methodref          #2.#25         // java/lang/String.intern:()Ljava/lang/String;
   #6 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = Methodref          #18.#28        // java/io/PrintStream.println:(Z)V
   #8 = Class              #29            // com/example/javac/StringExistInPoolBeforeIntern
   #9 = Class              #30            // java/lang/Object
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               StackMapTable
  #17 = Class              #31            // "[Ljava/lang/String;"
  #18 = Class              #32            // java/io/PrintStream
  #19 = Utf8               SourceFile
  #20 = Utf8               StringExistInPoolBeforeIntern.java
  #21 = NameAndType        #10:#11        // "<init>":()V
  #22 = Utf8               java/lang/String
  #23 = Utf8               tomcat
  #24 = NameAndType        #10:#33        // "<init>":(Ljava/lang/String;)V
  #25 = NameAndType        #34:#35        // intern:()Ljava/lang/String;
  #26 = Class              #36            // java/lang/System
  #27 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
  #28 = NameAndType        #39:#40        // println:(Z)V
  #29 = Utf8               com/example/javac/StringExistInPoolBeforeIntern
  #30 = Utf8               java/lang/Object
  #31 = Utf8               [Ljava/lang/String;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               (Ljava/lang/String;)V
  #34 = Utf8               intern
  #35 = Utf8               ()Ljava/lang/String;
  #36 = Utf8               java/lang/System
  #37 = Utf8               out
  #38 = Utf8               Ljava/io/PrintStream;
  #39 = Utf8               println
  #40 = Utf8               (Z)V
{
  public com.example.javac.StringExistInPoolBeforeIntern();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: ldc           #3                  // String tomcat
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1
        10: aload_1
        11: invokevirtual #5                  // Method java/lang/String.intern:()Ljava/lang/String;
        14: pop
        15: ldc           #3                  // String tomcat
        17: astore_2
        18: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        21: aload_1
        22: aload_2
        23: if_acmpne     30
        26: iconst_1
        27: goto          31
        30: iconst_0
        31: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        34: return
      LineNumberTable:
        line 11: 0
        line 13: 10
        line 14: 15
        line 15: 18
        line 16: 34
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 30
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "StringExistInPoolBeforeIntern.java"
  • 可以看到常量池有个tomcat

StringNotExistInPoolBeforeIntern

javac src/main/java/com/example/javac/StringNotExistInPoolBeforeIntern.java
javap -v src/main/java/com/example/javac/StringNotExistInPoolBeforeIntern.class

  Last modified 2019年4月6日; size 1187 bytes
  MD5 checksum 6d173f303b61b8f5826e54bb6ed5157c
  Compiled from "StringNotExistInPoolBeforeIntern.java"
public class com.example.javac.StringNotExistInPoolBeforeIntern
  minor version: 0
  major version: 56
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #11                         // com/example/javac/StringNotExistInPoolBeforeIntern
  super_class: #12                        // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool:
   #1 = Methodref          #12.#24        // java/lang/Object."<init>":()V
   #2 = Class              #25            // java/lang/String
   #3 = String             #26            // tom
   #4 = Methodref          #2.#27         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = String             #28            // cat
   #6 = InvokeDynamic      #0:#32         // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
   #7 = Methodref          #2.#33         // java/lang/String.intern:()Ljava/lang/String;
   #8 = String             #34            // tomcat
   #9 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
  #10 = Methodref          #21.#37        // java/io/PrintStream.println:(Z)V
  #11 = Class              #38            // com/example/javac/StringNotExistInPoolBeforeIntern
  #12 = Class              #39            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               StackMapTable
  #20 = Class              #40            // "[Ljava/lang/String;"
  #21 = Class              #41            // java/io/PrintStream
  #22 = Utf8               SourceFile
  #23 = Utf8               StringNotExistInPoolBeforeIntern.java
  #24 = NameAndType        #13:#14        // "<init>":()V
  #25 = Utf8               java/lang/String
  #26 = Utf8               tom
  #27 = NameAndType        #13:#42        // "<init>":(Ljava/lang/String;)V
  #28 = Utf8               cat
  #29 = Utf8               BootstrapMethods
  #30 = MethodHandle       6:#43          // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #31 = String             #44            // \u0001\u0001
  #32 = NameAndType        #45:#46        // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #33 = NameAndType        #47:#48        // intern:()Ljava/lang/String;
  #34 = Utf8               tomcat
  #35 = Class              #49            // java/lang/System
  #36 = NameAndType        #50:#51        // out:Ljava/io/PrintStream;
  #37 = NameAndType        #52:#53        // println:(Z)V
  #38 = Utf8               com/example/javac/StringNotExistInPoolBeforeIntern
  #39 = Utf8               java/lang/Object
  #40 = Utf8               [Ljava/lang/String;
  #41 = Utf8               java/io/PrintStream
  #42 = Utf8               (Ljava/lang/String;)V
  #43 = Methodref          #54.#55        // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #44 = Utf8               \u0001\u0001
  #45 = Utf8               makeConcatWithConstants
  #46 = Utf8               (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #47 = Utf8               intern
  #48 = Utf8               ()Ljava/lang/String;
  #49 = Utf8               java/lang/System
  #50 = Utf8               out
  #51 = Utf8               Ljava/io/PrintStream;
  #52 = Utf8               println
  #53 = Utf8               (Z)V
  #54 = Class              #56            // java/lang/invoke/StringConcatFactory
  #55 = NameAndType        #45:#60        // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #56 = Utf8               java/lang/invoke/StringConcatFactory
  #57 = Class              #62            // java/lang/invoke/MethodHandles$Lookup
  #58 = Utf8               Lookup
  #59 = Utf8               InnerClasses
  #60 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #61 = Class              #63            // java/lang/invoke/MethodHandles
  #62 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #63 = Utf8               java/lang/invoke/MethodHandles
{
  public com.example.javac.StringNotExistInPoolBeforeIntern();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: ldc           #3                  // String tom
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: new           #2                  // class java/lang/String
        12: dup
        13: ldc           #5                  // String cat
        15: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        18: invokedynamic #6,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        23: astore_1
        24: aload_1
        25: invokevirtual #7                  // Method java/lang/String.intern:()Ljava/lang/String;
        28: pop
        29: ldc           #8                  // String tomcat
        31: astore_2
        32: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        35: aload_1
        36: aload_2
        37: if_acmpne     44
        40: iconst_1
        41: goto          45
        44: iconst_0
        45: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        48: return
      LineNumberTable:
        line 11: 0
        line 13: 24
        line 14: 29
        line 15: 32
        line 16: 48
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 44
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "StringNotExistInPoolBeforeIntern.java"
InnerClasses:
  public static final #58= #57 of #61;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #30 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #31 \u0001\u0001
  • 可以看到常量池有tom、cat、tomcat

小结

  • 当调用intern方法时,如果常量池已经包含一个equals此String对象的字符串,则返回池中的字符串
  • 当调用intern方法时,如果常量池没有一个equals此String对象的字符串,将此String对象添加到池中,并返回此String对象的引用(即intern方法返回指向heap中的此String对象引用)
  • 所有literal strings及string-valued constant expressions都是interned的

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...