常量池和StringTable

1 运行时常量池

  • 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

  • 运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

2 StringTable(串池)

先看一道面试题:

String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";
String s4 = s1 + s2;
String s5 = "ab";
String s6 = s4.intern();
// 问
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
String x2 = new String("c") + new String("d");
String x1 = "cd";
x2.intern();
// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
System.out.println(x1 == x2);

答案是:false true true false(调换后true)

上面程序使用javap -v反编译后得到JVM指令如下:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=9, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: ldc           #4                  // String ab
        31: astore        5
        33: aload         4
        35: invokevirtual #9                  // Method java/lang/String.intern:()Ljava/lang/String;
        38: astore        6
        40: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        43: aload_3
        44: aload         4
        46: if_acmpne     53
        49: iconst_1
        50: goto          54
        53: iconst_0
        54: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        57: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        60: aload_3
        61: aload         5
        63: if_acmpne     70
        66: iconst_1
        67: goto          71
        70: iconst_0
        71: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        74: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        77: aload_3
        78: aload         6
        80: if_acmpne     87
        83: iconst_1
        84: goto          88
        87: iconst_0
        88: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        91: new           #5                  // class java/lang/StringBuilder
        94: dup
        95: invokespecial #6                  // Method java/lang/StringBuilder."":()V
        98: new           #12                 // class java/lang/String
       101: dup
       102: ldc           #13                 // String c
       104: invokespecial #14                 // Method java/lang/String."":(Ljava/lang/String;)V
       107: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       110: new           #12                 // class java/lang/String
       113: dup
       114: ldc           #15                 // String d
       116: invokespecial #14                 // Method java/lang/String."":(Ljava/lang/String;)V
       119: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       122: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       125: astore        7
       127: ldc           #16                 // String cd
       129: astore        8
       131: aload         7
       133: invokevirtual #9                  // Method java/lang/String.intern:()Ljava/lang/String;
       136: pop
       137: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
       140: aload         8
       142: aload         7
       144: if_acmpne     151
       147: iconst_1
       148: goto          152
       151: iconst_0
       152: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
       155: return

分析:

s1引用的是串池中的常量"a"

s2引用的是串池中的常量"b"

s3引用的是编译时优化的"a"“b"拼接后的串池中的常量"ab”

s4引用的是堆中StringBuilder对象调用init方法初始化,并append"a",又append"b",最后调用toString得到的String对象

s5引用的是串池中的常量"ab"

s6,由于s4的对象内容"ab"在s3执行时就已经入池,因此返回串池中的"ab",即s3的地址

x2引用的是堆中StringBuilder对象调用init方法初始化,并append堆中创建的"c"和"d"String对象,最后调用toString得到的String对象

x1引用的是串池中的常量"cd"

x2.intern();执行完上一句后,此时串池中已有"cd",不会将x2放入串池,x2仍引用堆中的对象地址

注意1:若执行x2.intern();后再String x1 = “cd”;则两个变量引用的都是串池中的"cd"的地址

注意2:以上都是jdk1.8版本的答案,如果是jdk1.6,在String x2 = new String(“c”) + new String(“d”);后,先执行x2.intern();再String x1 = “cd”;分析为:x2.intern()将堆中对象拷贝到串池中,导致x1==x2结果为false

intern方法说明:

功能:将串池中还没有的字符串对象放入串池

1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回

StringTable总结

位置 GC 调优
1.8版本位于堆中
1.6版本只存在于老年代
1.8时触发minorGC
1.6时触发fullGC
增大堆内存的同时
适当增加StringTableSize桶个数可以减少寻址时的hash碰撞概率,从而节省读写时间
intern方法使对象入池复用(大量重复情况),可以节省大量创建新对象占用堆内存

你可能感兴趣的:(jvm,java,jvm)