从字节码分析字符串是否相等

Posted 364.99°

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从字节码分析字符串是否相等相关的知识,希望对你有一定的参考价值。

1. 简单分析创建字符串的虚拟机指令

1. 字节码的简单阅读

先看一段简单程序及其字节码,了解字节码的简单阅读:javap -c Demo1.class


javap -v xxx.class 反编译,展示字节码的详细信息。
详细信息内容:类的基本信息、常量池、类方法定义、虚拟机指令

如何读字节码:

  • 补充说明:# 后面有两个数字,比如说 # 3, 2 ,这是说明 这里调用了常量池的 # 3的值,而 # 3 的值又对应了两个常量符号。

常量池: 给二进制字节码指令(虚拟机指令)提供一些常量符号,根据常量符号表的查找方式去找到目标值。

2. 字符串的创建过程

javap -v TestStringTable.class

  • 常量池中的信息,都会被加载到运行时常量池中,这时的 1、2、1还没有变成字符串对象
  • ldc #2 会把对应的符号变成一个对象
  • astore_1 会将1第个引用类型本地变量推送至栈顶(就是存储到 Slot=1的地方)

经过上述过程,可发现,只有在用到变量的时候,才会创建为java对象。

2. 为什么不相等

a + b != “12”

追加下面的代码,并反编译。

String d = a + b;

读取过程:

  • new 一个 StringBuilder 对象
    dup,复制栈顶数值并将复制值压入栈顶

  • invokespecial 调用实例方法"<init>":()V——StringBuilder的无参构造方法

  • aload_1 调用栈帧slot为1的变量(a)的值

  • invokespecial 调用实例方法append

  • aload_2 调用栈帧slot为2的变量 (b) 的值

  • invokespecial 调用实例方法append

  • invokespecial 调用实例方法toString

  • astore 4 将toString后的值存入栈帧Slot为4的变量(d)

分析:

  • d的字符串对象存在堆里面

  • 如果是直接定义一个字符串变量

    String e = "12"
    

    那么这个 “12” 是存在常量池中的

  • 所以 e == d 结果为 false

“1” + “2” = “12”

追加代码

        String e = "1" + "2";
        String f = "12";

反编译:

可以发现,两者都是直接从常量池中创建 “12” 对象,然后将对象传给 e 和 f。
所以 e == f 为 true。

为什么JVM对两者的处理不同?

这都是 javac 优化编译过程的结果:

  • String e = "1" + "2"; 直接从常量池取值的原因:javac在编译期间的优化,其结果在编译的时候,就已经知道了(在编译期间就已经拼接了)
  • String d = a + b; ,a 和 b 都是变量,值可能会改变,为了保证结果的正确性,就使用 StringBuilder 来进行动态拼接

注意: 可以通过 str.intern() 将动态拼接、存在堆中的字符串对象放入串池

  • 如果串池中有该字符串对象,就不会放入

  • 如果串池中没有该字符串对象,就会放入,也会将池中的对象返回

    String a = "1";
    String b = new String("1");
    boolean flag1 = a == b;// return false
    String b1 = b.intern();// 放入串池
    boolean flag2 = a == b1;// return true
    
    String c = new String("1") + new String("2");
    c.intern();
    boolean flag1 = "12" == c;//return  true
    
    String d = "123";
    String e = new String("12") + new String("3");
    e.intern();
    boolean flag3 = "123" == e;//return false
    

注意: 在1.6,str.intern()

  • 如果串池中有该字符串对象,就不会放入
  • 如果串池中没有该字符串对象,就会先复制一份,再将复制的那份放入(这就会导致放入的对象与原对象不相等),也会将池中的对象返回

以上是关于从字节码分析字符串是否相等的主要内容,如果未能解决你的问题,请参考以下文章

python 学习DAY06

golang unicode/utf8源码分析

66.javac 编译与 JIT 编译编译过程javac 编译词法语法分析填充符号表语义分析字节码生成JIT 编译

String 部分源码分析

IntelliJ 中的目标字节码总是回落到 1.5

Java字节码常量池深度剖析与字节码整体结构分解