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, ...
随机推荐
- ASP.NET MVC 控制器激活(三)
ASP.NET MVC 控制器激活(三) 前言 在上个篇幅中说到从控制器工厂的GetControllerInstance()方法来执行控制器的注入,本篇要讲是在GetControllerInstanc ...
- ASP.NET Web API自身对CORS的支持:从实例开始
在<通过扩展让ASP.NET Web API支持W3C的CORS规范>中我们通过自定义的HttpMessageHandler为ASP.NET Web API赋予了跨域资源共享的能力,具体来 ...
- 微信小程序产品定位及功能介绍
产品定位及功能介绍 微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 小程序注册 注册小程序帐号 在微信公众平台官网首页(mp.weixin.qq ...
- Linux内核目录结构
arch 包括所有和体系结构相关的核心代码. include 包括编译内核所需要的大部分头文件 init 包含内核的初始化代码(不是系统的引导代码),有main.c和Version.c两个文件 mm ...
- 动态加载JS 和 CSS
<script type="text/javascript"> $(function () { var filename = '/assets/css/main.css ...
- 深入学习jQuery节点关系
× 目录 [1]后代元素 [2]祖先元素 [3]兄弟元素 前面的话 DOM可以将任何HTML描绘成一个由多层节点构成的结构.节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形 ...
- 浅谈web语义化
在前端的编程道路上,是否听过html的结构语义化? 是否觉得自己前端嘛,只要做出炫酷的效果,编写出牛逼的JavaScript代码就ok啦.div+css所向无敌,干嘛要用其他标签呢. 是啊,正如上面所 ...
- Android随笔之——获取EditText光标所在行行号
由于项目需求,需要获取EditText光标当前所在行行号,可是翻遍Android文档.问遍度娘都没发现,于是在博客园中提问,碰见了好心人告诉了我答案,谨以以下代码献给有需要的人 private int ...
- MySQL基于时间字段进行分区的方案总结
MySQL支持的分区类型一共有四种:RANGE,LIST,HASH,KEY.其中,RANGE又可分为原生RANGE和RANGE COLUMNS,LIST分为原生LIST和LIST COLUMNS,HA ...
- RHEL 6.3 详细安装教程
以前刚接触linux时,什么都不懂,为了学习,在电脑上安装双系统(原系统为Win7),吃过不少苦头,在网上搜教程,很多都是语焉不详,导致安装过程中战战兢兢.最近朋友面试运维,面试官有考他对linux安 ...