聊聊java String的intern


本文主要研究一下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

浅谈String的intern
Why does String.intern() return different results under JDK8 and JDK9?
How to Initialize and Compare Strings in Java?
Difference between String literal and New String object in Java
聊聊jvm的PermGen与Metaspace
Guide to Java String Pool
String Literal Vs String Object in Java

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理