有关String.intern的基础知识在这里:
深入解析String#intern
这篇文章只是我的一个笔记,做一个补充,并对其中的不赞同的观点做一个描述。代码版本是JDK8。
public class Test {
public static void main(String[] args) {
String a = "myStrAAA";
String myStr = new String("myStrAAA");
String myStr1 =new String("myStrAAA").intern();
System.out.println(a==myStr);
System.out.println(a==myStr1);
while(true){
}
}
}
输出: false true
首先来说,从纯粹的语法的角度来讲,应该是new 了 3个对象。但实际上通过MAT分析发现只生成了2个对象:
这是因为JVM编译器会做优化
new String("myStrAAA").intern();
执行的时候并从左到右执行,而是JVM在执行这段代码的时候会做优化,先去判断运行常量池里面有没有,如果有就直接取出这个对象,如果没有,就生成指定的字符串,放到常量池中间再返回。
通过javap反汇编可以确定JVM对含有intern的语句的优化操作:
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String myStrAAA
2: astore_1
3: new #3 // class java/lang/String
6: dup
7: ldc #2 // String myStrAAA
9: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V
12: astore_2
13: new #3 // class java/lang/String
16: dup
17: aload_2
18: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V
21: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String;
24: astore_3
25: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_1
29: aload_2
30: if_acmpne 37
33: iconst_1
34: goto 38
37: iconst_0
38: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
41: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
44: aload_1
45: aload_3
46: if_acmpne 53
49: iconst_1
50: goto 54
53: iconst_0
54: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
57: return
}
整个方法中只出现了两个new操作符。说明只生成了两个对象实例。
对于那些大量含有重复字符串的程序来说,使用intern不但节省空间,而且更加快速(所以在深入解析String#intern
中一文说使用intern会变慢,不敢苟同,而且下面的代码实际运行也证明了这一点):
public class TestIntern2 {
static final int MAX = 10737410;
static final String[] arr = new String[MAX];
public static void main(String[] args) throws Exception {
Integer[] sample = new Integer[10];
Random random = new Random(1000);
for (int i = 0; i < sample.length; i++) {
sample[i] = random.nextInt();
}
// 记录程序开始时间
long t = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++) {
arr[i] = new String(String.valueOf(sample[i % sample.length])).intern();
}
System.out.println((System.currentTimeMillis() - t) + "ms");
}
}
这段代码含有大量的重复字符串,使用intern可以减少对象的生成和空间的占用。
我在我的机器上执行时间是2269ms。如果不使用intern,我在我的机器上是8222ms。对于使用intern和不使用intern的差别也可以通过jvisualvm来查看:
在JDK7以后,有这么一个情况:
public class Test {
public static void main(String[] args) {
String str2 = new String("str") + new String("01");
str2.intern();
String str1 = "str01";
System.out.println(str2 == str1);// #7
}
}
返回的结果是true。
public class Test {
public static void main(String[] args) {
String str1 = "str01";
String str2 = new String("str") + new String("01");
str2.intern();
System.out.println(str2 == str1);
}
}
返回了 false。
这种情况在http://www.importnew.com/21024.html 有描述,我就不提了。
如果有什么问题,欢迎在评论区讨论。