JVM-String比较-字节码分析
一道String字符串比较问题引发的字节码分析
public class a {
public static void main(String[] args)throws Exception{
}
public static void aa(){
String s1="a";//内存在方法区的常量池
String s2="b";//内存在方法区的常量池
String s12 = "ab";//内存在方法区的常量池
String s3 = s1 + s2;//s3的内存所在???
p(s3==s12);//false
}
public static void bb(){
String s1="a"+"b";//s1的内存所在???
String s2 = "ab";//内存在方法区的常量池
p(s1==s2);//true
}
public static void p(Object obj){
System.out.println(obj);
}
}
这是我们经常碰到的烦人的String比较问题,要得到答案,就要弄清楚aa()方法中的s3的内存在哪里?,和bb()方法中的s1的内存在哪里?
不多说,贴上a.class文件反编译的字节码指令:
首先是 aa()方法:
public static void aa();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=0 共4个本地变量空间
0: ldc #3 // String a 将字符串"a"从常量池中推送至栈顶
2: astore_0 将栈顶引用类型(即字符串"a")存入第一个本地变量
3: ldc #4 // String b 将字符串"b"从常量池推送至栈顶
5: astore_1 将栈顶引用类型(即字符串"b")存入第二个本地变量
6: ldc #5 // String ab 将字符串"ab"从常量池推送至栈顶
8: astore_2 将栈顶引用类型(即字符串"ab")存入第三个本地变量
9: new #6 // class java/lang/StringBuilder 创建StringBuilder对象sb,并将引用值压入栈顶
12: dup 复制栈顶数值,并将复制值压入栈顶
13: invokespecial #7 // Method java/lang/StringBuilder. 调用对象的初始化方法
"<init>":()V
16: aload_0 将第一个本地变量(即字符串"a")推送至栈顶
17: invokevirtual #8 // Method java/lang/StringBuilder. 调用实例方法sb.append("a");
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_1 将第二个本地变量(即字符串"b")推送至栈顶
21: invokevirtual #8 // Method java/lang/StringBuilder. 调用实例方法sb.append("b");
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #9 // Method java/lang/StringBuilder. 调用实例方法sb.toString(),并将结果【Java堆地址】放在栈顶
toString:()Ljava/lang/String;
27: astore_3 将栈顶引用类型(即堆地址)存入第四个本地变量
28: aload_3 将第四个本地变量(即堆地址)推送至栈顶
29: aload_2 将第三个本地变量(即字符串"ab")推送至栈顶
30: if_acmpne 37 比较栈顶两引用数值,结果不同跳转(当然不同啦)
33: iconst_1
34: goto 38
37: iconst_0 将int类型 0 压入栈顶
38: invokestatic #10 // Method java/lang/Boolean.valueO 调用静态方法Boolean.valueOf();实现基本数据类型->包装类型自动转换
f:(Z)Ljava/lang/Boolean;
41: invokestatic #11 // Method p:(Ljava/lang/Object;)V 调用静态方法p(false);//输出false
44: return 从当前方法返回void
针对其中的一些解释:(下面的数字是字节码偏移量)
24 为何在sb.toString()我说的是【堆地址】,大家看源码就知道了。
//这是StringBuilder的源码,返回的是堆上的字符串地址
public String toString() {
return new String(value, 0, count);
}
所以在aa()方法中,s3的内存其实在Java堆上,s12在方法区的常量池上,所以两者不相等。
37 boolean到底分配几个字节,在这里大家可以看到。
如果为true,编译器翻译的字节码是iconst_1,意思将int类型1存入栈顶,所以单个引用boolean值时,分配4个字节,和int相同。(数组boolean没测试,不清楚)
如果为false,编译器翻译的字节码是iconst_0,意思将int类型0存入栈顶。
38 在这里我们还能看到自动类型转换的身影,这里是基本数据类型boolean->包装类Boolean的自动类型转换,实际调用的就是Boolean.valueOf()静态方法,这是因为下面的p()方法里面需要的是Object引用类型,所以进行了自动类型转换。
然后是 bb()方法:
public static void bb();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=0 两个本地变量空间
0: ldc #5 // String ab 将字符串"ab"从常量池中推送至栈顶
2: astore_0 将栈顶引用类型(字符串"ab")存入第一个本地变量
3: ldc #5 // String ab 将字符串"ab"从常量池中推送至栈顶
5: astore_1 将栈顶引用类型(字符串"ab")存入第一个本地变量
6: aload_0 将第一个本地变量("ab")推送至栈顶
7: aload_1 将第二个本地变量("ab")推送至栈顶
8: if_acmpne 15 比较栈顶两引用类型数值,结果不同跳转(这里当然相同啦)
11: iconst_1 将int类型 1 推送至栈顶
12: goto 16 无条件跳转到16字节码偏移量
15: iconst_0
16: invokestatic #10 // Method java/lang/Boolean.valueO 调用静态方法Boolean.valueOf();并将返回的Boolean类型的true压入栈顶
f:(Z)Ljava/lang/Boolean;
19: invokestatic #11 // Method p:(Ljava/lang/Object;)V 调用静态方法p(true);输出true
22: return 从当前方法返回void
针对其中的一些解释:(下面的数字是字节码偏移量)
0 大家看到了吧,编译器看到String a="aa"+"bb";会自动合并,将"aabb"存入常量池,并返回地址。所以答案为true。
----------------------------------------------------新添加内容分割线-----------------------------------------------------------------
我在这篇博文中JVM-绘图展现方法内部字节码执行具体画了上面的bb()方法的字节码执行流程,如果大家对执行过程感兴趣,欢迎大家访问。
JVM-String比较-字节码分析的更多相关文章
- Java并发编程原理与实战八:产生线程安全性问题原因(javap字节码分析)
前面我们说到多线程带来的风险,其中一个很重要的就是安全性,因为其重要性因此,放到本章来进行讲解,那么线程安全性问题产生的原因,我们这节将从底层字节码来进行分析. 一.问题引出 先看一段代码 packa ...
- Java字节码分析
目录 Java字节码分析 查看字节码详细内容 javap 实例分析 Java字节码分析 对于源码的效率,但从源码来看有时无法分析出准确的结果,因为不同的编译器版本可能会将相同的源码编译成不同的字节码, ...
- 通过字节码分析this关键字以及异常表的重要作用
在之前的字节码分析中缺少对异常的介绍,这次主要来对字节码异常表相关的东东进行一个学习,下面先来编写一个相关异常的小程序: 接着编译来看用javap -verbose来查看一下它的字节码信息: xion ...
- <JVM中篇:字节码与类的加载篇>04-再谈类的加载器
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- <JVM中篇:字节码与类的加载篇>03-类的加载过程(类的生命周期)详解
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- 通过字节码分析java中的switch语句
在一次做题中遇到了switch的问题,由于对switch执行顺序的不了解,在这里简单的通过字节码的方式理解一下switch执行顺序(题目如下): public class Ag{ static pub ...
- <JVM中篇:字节码与类的加载篇>02-字节码指令集
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- JVM总括三-字节码、字节码指令、JIT编译执行
JVM总括三-字节码.字节码指令.JIT编译执行 目录:JVM总括:目录 java文件编译后的class文件,java跨平台的中间层,JVM通过对字节码的解释执行(执行模式,还有JIT编译执行,下面讲 ...
- Python_Tips[2] -> 函数延后估值及字节码分析
函数延后估值及字节码分析 在一个循环中定义了函数 f 但是并未对其进行调用,在循环结束后调用,此时i值为3故最终3个函数输出均为9.而非1, 4, 9. 这是由于在定义闭包函数 f 时,传入变量 i, ...
随机推荐
- The Hacker's Guide To Python 单元测试
The Hacker's Guide To Python 单元测试 基本方式 python中提供了非常简单的单元测试方式,利用nose包中的nosetests命令可以实现简单的批量测试. 安装nose ...
- .NET陷阱之六:从枚举值持久化带来大量空间消耗谈起
好长时间没有写博文了,今天继续. 这次跟大家分享的内容起因于对一个枚举值列表的序列化,下面简化后的代码即能重现.为了明确起见,我显式指定了枚举的基础类型. // 定义一个枚举类型. public en ...
- [ASP.NET MVC 小牛之路]09 - Controller 和 Action (1)
我们知道,在 MVC 中每个请求都会提交到 Controller 进行处理.Controller 是和请求密切相关的,它包含了对请求的逻辑处理,能对 Model 进行操作并选择 View 呈现给用户, ...
- Package Configurations的使用示例
SSIS提供Package Configurations功能,能够动态修改配置数据,使package执行不同的变量,从不同的connection中获取数据,而这些变化,不需要修改Package,不需要 ...
- JS动态生成的元素,其对应的方法不响应(比如单击事件,鼠标移动事件等)
主要原因:在页面给元素注册点击事件的时候[ $(function () { XXX }); ],JS动态生成的元素还尚未生成,所以click事件就没有生效 解决方法: 方案一:js动态生成元素后再给 ...
- SQL Server 2014新特性探秘(2)-SSD Buffer Pool Extension
简介 SQL Server 2014中另一个非常好的功能是,可以将SSD虚拟成内存的一部分,来供SQL Server数据页缓冲区使用.通过使用SSD来扩展Buffer-Pool,可以使得大量随 ...
- 轻量级前端MVVM框架avalon - 初步接触
迷你简单易用的MVVM框架 avalon的介绍http://rubylouvre.github.io/mvvm/ 按照作者的介绍,在HTML中添加绑定,在JS中用avalon.define定义View ...
- 【转】C#调用Windows图片和传真查看器打开图片
//建立新的系统进程 System.Diagnostics.Process process = new System.Diagnostics.Process(); //设置文件名,此处为图片的真实路径 ...
- 启动第一个 KVM 虚机 - 每天5分钟玩转 OpenStack(4)
本节演示如何使用 virt-manager 启动 KVM 虚机. 首先通过命令 virt-manager 启动图形界面 # virt-manager 点上面的图标创建虚机 给虚机命名为 kvm1,这里 ...
- Web APi之控制器选择Action方法过程(九)
前言 前面我们叙述了关于控制器创建的详细过程,在前面完成了对控制器的激活之后,就是根据控制器信息来查找匹配的Action方法,这就是本节要讲的内容.当请求过来时首先经过宿主处理管道然后进入Web AP ...