JVM StringTab

StringTab 和intern()

根据几个实例来帮助我们理解StringTab在JVM中的运行方法


//JDK1.8
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);//false
System.out.println(s3 == s5);//true
System.out.println(s3 == s6);//true

String x2 = new String("c") + new String("d");
String x1 = "cd";
x2.intern();

System.out.println(x1 == x2);//false

分析:

编译反编译java文件:

public class StingTab {
    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
    }
}
  1. javac -g StingTab.java
  2. ,javap -v StingTab.class
//常量池
Constant pool:
   #1 = Methodref          #6.#24         // java/lang/Object."<init>":()V
   #2 = String             #25            // a
   #3 = String             #26            // b
   #4 = String             #27            // ab
   #5 = Class              #28            // com/wghcwc/gc/StingTab
   #6 = Class              #29            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/wghcwc/gc/StingTab;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               s1
  #19 = Utf8               Ljava/lang/String;
  #20 = Utf8               s2
  #21 = Utf8               s3
  #22 = Utf8               SourceFile
  #23 = Utf8               StingTab.java
  #24 = NameAndType        #7:#8          // "<init>":()V
  #25 = Utf8               a
  #26 = Utf8               b
  #27 = Utf8               ab
  #28 = Utf8               com/wghcwc/gc/StingTab
  #29 = Utf8               java/lang/Object
  ......
  //构造方法..略
  ....
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=4, args_size=1
      //去常量池 加载#2位置对应符号------> a ,将a符号变成字符串对象
         0: ldc           #2                  // String a
         //把上一步加载的 a 存入局部变量表slot变黄为 1的位置 args s1 
         对应代码   String s1 = "a";
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: return
         //行号
      LineNumberTable:
        line 10: 0
        line 11: 3
        line 12: 6
        line 13: 9
        //局部变量表
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  args   [Ljava/lang/String;
            3       7     1    s1   Ljava/lang/String;
            6       4     2    s2   Ljava/lang/String;
            9       1     3    s3   Ljava/lang/String;
}

根据上一篇,JVM内存结构
我们知道,字节码的中的常量池(Constant pool)会在类加载后加载到运行时常量池.但是,在加载到运行时常量池时,字节码常量池中的字符串符号并没有成为java 字符串对象,直到运行到相应代码时,才会把对应的字符串符号转换成java对象,并放入StringTab,所以上面代码执行完毕,StringTab 中将包含{“a”,“b”,“ab”}

接下来 添加一句String s4 = "a" + "b"; 看字节码

public class StingTab {
    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        String s4 = "a" + "b";
    }
}
  1. javac -g StingTab.java
  2. ,javap -v StingTab.class
 stack=1, locals=5, 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: ldc           #4                  // String ab
        11: astore        4
+++++++++++++++++++++
        13: return

可以看到

 		String s3 = "ab";
        String s4 = "a" + "b";

所对应的字节码是一样的,值引用了同一个地址#4的数据
所以可以毫不费力的得出 s3=s4

我们继续分析 ,编译反编译如下代码

public class StingTab {
    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        String s4 = "a" + "b";
        String s5 = s1 + s2;
    }
}
      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: ldc           #4                  // String ab
        11: astore        4
        +++++++++++++++++++++++++++++
        创建/StringBuilder 实例
        13: new           #5                  // class java/lang/StringBuilder
        //复制一份StringBuilder 实例
        16: dup
        //调用构造函数,此时会消耗一份StringBuilder 实例,也是上一步的原因
        17: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        //参数 a 
        20: aload_1
        //调用append
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: aload_2
        25: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        28: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        31: astore        5
        33: return

可以看到String s5所对应的字节码多了很多,根据后面的注释我们不难猜出是根据StringBuilder实现的两个字符串对象相加…
String s5 = s1 + s2; 等价于new StringBuilder().append("a").append("b").toString();
StringBuilder 的toString()方法新建了一个String对象,储存在堆中.
所以s4!=s5.

StringTab JDK1.8 intern() 存的是对象还是引用?

    public static void main(String[] args) throws InterruptedException {
        test();
        System.gc();
        System.out.println(System.identityHashCode("ab"));

    }

    public static String test(){
        String testA = new String("a") + new String("b");
        System.out.println(System.identityHashCode(testA));
        String testB = testA.intern();
        System.out.println(System.identityHashCode(testB));
        return testB;
    }

输出

1321522194
1321522194
935563448

可知,当StringTab 不存在某个字符串时,intern 存入的是当前对象的引用,当垃圾回收时,该引用指向堆中对象会被正常回收.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值