来自:烟雨星空

前言

我们平时编码过程中,可能很少去查看 Java 文件编译后的字节码指令。但是,不管你是因为对技术非常热爱,喜欢刨根问底,还是想在别人面前装X 。我认为,都非常有必要了解一下常见的字节码指令。这对于我们理解代码的运行原理也会很有帮助。

注释会被执行吗?

比如,最近我就看到了二哥(沉默王二)的一篇文章,非常有意思,也让我涨了见识了。且看代码,可能会让你怀疑人生~

public class TestAnno {public static void main(String[] args) {String str = "张三";// \u000d str="李四";System.out.println(str);}
}

可以猜一下最终打印结果是什么。我来告诉你,结果是:“李四”。What?这是什么神仙操作。为何会这样,这被注释的代码还能执行?

别光顾着吃惊,我们来看一下为什么打印结果跟我们想象中的略有偏差。

仔细查看,发现注释行中,有一个 \u000d 。这是一个 unicode 码,它代表的是一个换行符。因此,在执行后,就会把 str="李四" 这行代码换行到下一行,从而导致了它也被编译执行。

有的小伙伴就说了,我怎么知道你说的是真是假呢,你这不是瞎说呢吗,一点说服力都没有啊。

好吧,这就需要我们看一下编译后的字节码了,编译器总不能骗我们吧。

查看字节码的两种方式

我们知道,通过 javac 命令,可以把 java 文件编译成 class 文件。然后,想看它的字节码指令,需要用到 javap -c 反编译一下。

但是,每次都通过命令行,还是有一些麻烦。因此,懒人福利来了。这里我提供两种方式,直接在 idea 中就可以查看,非常的方便(果然,懒惰才是促使科技进步的原动力啊)。

配置 External Tools

这种方式,其实就是先配置一下,JDK 中 javap 命令的位置,然后让 idea 帮我们自动执行 javap 命令。

找到 idea 的菜单栏,File --> Settings 。然后找到 External Tools 。

点击加号,添加一个 tool ,里边的配置项,如下,

  • Name,就给当前 tool 起一个名字好了,用于使用时,在右键菜单选项中展示。

  • Decriptions,描述此功能,别到时候你自己都忘了当前配置这个东西是用来干嘛的了。

  • Program:电脑本机 javap 命令的绝对路径。

  • Arguments:填写 -c $FileNameWithoutExtension$.class 就可以,代表编译的class文件。

  • Working directory:填写 $OutputPath$/$FileDirRelativeToSourcepath$ ,代表class文件的工作目录。

配置完成了,可以在idea中,直接右键,选择配置中 Name 对应的选项。

此时 ,就可以打印出当前类编译后的字节码指令。

我们稍后再来分析里边的具体含义。下边,还有另外一种使用插件的方式,来查看字节码。而且功能更加强大。

jclasslib bytecode viewer 插件

同上,在 idea 的 setting 菜单,找到 Plugins 插件项,然后搜索这个插件。

安装之后,并重启 idea ,就可以生效了。

使用的时候,在 View 菜单,找到 show bytecode with jclasslib。

它可以查看基本信息,常量池,接口,属性,方法等信息。如下,找到我们对应的 main 方法。

这个插件还有一个好处,就是当我们不知道某个指令是什么意思的时候,直接鼠标在指令上边单击,就可以链接到官方的解释。如,不知道第一行 ldc 什么意思,直接单击 ldc 就可以跳转到 oracle 官网对应命令的解释。这非常有利用我们学习此命令。

常用字节码指令

以上两种方式,都挺方便的,根据自己喜好自由选择就好了。

相信你肯定也被很多字节码指令搞的一头雾水过,接下来,我们就一起学习下常用的指令都有哪些吧。之后,再来看上边的例子就轻松很多了。

数据类型

我们知道 Java 是强类型语言,在使用之前肯定已经确定了它的类型。而数据类型,无非就是基本数据类型和引用类型。它们对应的字节码,其实就是用它们的对应类型的英文首字母来表示的。(引用类型除外)

例如,s 代表 short , i 代表 int, l 代表 long ,f 代表 float,d 代表 double,b 代表 byte(不包括boolean),c 代表 char,a 代表 reference 引用。

加载和存储指令

我们知道,一个方法的运行,会在栈的栈帧中执行。方法中的变量称为局部变量,数的操作需要用到操作数栈。因此,加载和存储指令,就是数据在局部变量表和操作数栈中来回传输。

将一个局部变量加载到操作数栈:如 iload、iload_  。表示加载的是 int 类型变量。

iload_  后边带数字代表第几个 int 型变量。例如 iload_0 代表把第一个 int 型局部变量加载到操作数栈。

其他类型变量同上:lload ,fload ,dload,aload 。

将一个数值存储到局部变量表:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstore_,astore,astore_ 。

将一个常量加载到操作数栈:aconst_null,iconst_m1,iconst_,lconst_,fconst_,dconst_ ,bipush,sipush。

ldc 代表把 int、float、String类型常量从常量池中加载到操作数栈。ldc_w 代表宽索引。

ldc2_w 代表把 long 或 double 类型常量从常量池中加载到操作数栈。(宽索引)

这里需要说的是,int 类型根据数值的取值范围不同,而采用不同的字节码指令。

iconst_m1 代表 -1 ,iconst_ 代表 0~5 。bipush 代表 -128~127(byte取值范围), sipush 代表 -32768~32767(short取值范围),ldc 在 int 中代表 -2147483648~2147483647(int取值范围)。

public class TestByteCode {public static void main(String[] args) {int a = -1;int b = 0;int c = 1;int d = 2;int e = 3;int f = 4;int g = 5;int h = 127;int i = 32767;int j = 2147483647;}
}

其字节码为:

0 iconst_m11 istore_12 iconst_03 istore_24 iconst_15 istore_36 iconst_27 istore 49 iconst_3
10 istore 5
12 iconst_4
13 istore 6
15 iconst_5
16 istore 7
18 bipush 127
20 istore 8
22 sipush 32767
25 istore 9
27 ldc #2 <2147483647>
29 istore 10
31 return

访问指令

访问类字段:getstatic,putstatic

访问类实例字段:getfield,putfield

方法调用和返回

  • invokevirtual :用于调用对象的实例方法。

  • invokeinterfce:用于调用接口方法。

  • invokespecial:用于调用一些特殊的方法,如父类构造方法,实例初始化方法,私有方法。

  • invokestatic:用于调用类的静态方法。

  • invokedynamic:用于调用动态方法。

方法返回是跟返回类型相关,根据不同的返回类型,有不同的指令。

  • return:返回 void。

  • ireturn:注意,这个不止返回 int ,返回 boolean,byte,char,short 也用这个指令。其实,很多指令都没有直接支持 byte,char,short和 boolean,而是用 int 类型代替。这是因为,虚拟机的操作码长度只有一个字节,只能表示有限个数的指令。(我们这里所提到的所有指令,都只是方便我们记忆的助记符,而在计算机内部肯定还是一个字节,即 8 个 bit 位的二进制)

  • lreturn:返回 long 类型。

  • freturn:返回 float 类型。

  • dreturn:返回 double 类型。

  • areturn:返回引用类型。

其余字节码指令

上边介绍的指令只是很少一部分字节码指令。但是,麻雀虽小,五脏俱全,也包括了最基本的变量定义,调用方法,和方法返回这些最基本的功能。同时,也足够我们去解释上边的问题,为什么注释行会被执行了。

更多的字节码指令可以参考《深入理解Java虚拟机》这本书的 6.4 节,和最后的附录字节码指令表。需要这本书的,可以在公众号后台回复“Java虚拟机”获取。

由于字节码指令太多,比如还有运算指令,包括加减乘除、位运算,比较指令等,if 等控制指令,类型强转指令,还有多线程用到的同步锁。And  so on ~

不可能把它们全部记住背会,但是,其实都是由规律可循的,很多都是见名知意,用各种英文首字母简写代表。比如,int类型的加法运算,就是 iadd,double 类型的减法运算是 dsub。等等。

因此,我这里只是给了一个引子,重要的还是需要自己去寻找方法,不断的实践探索 ~

另外,官方文档才是最好的学习途径:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html

注释行字节码

最后,学以致用,实践出真知。我们一起看一下开头抛出的问题吧,为什么注释行会被执行。看一下它的字节码指令就知道了。

public class TestAnno {public static void main(String[] args) {String str = "张三";// \u000d str="李四";System.out.println(str);}
}
 //这里就是从常量池中取出一个字符串“张三”,然后加载到操作数栈0 ldc #2 <张三>//从操作数栈把这个字符串引用存储到局部变量表中2 astore_1//从常量池中取出一个字符串“李四”,然后加载到操作数栈3 ldc #3 <李四>//可以发现和上边一样都是astore_1,说明指向的是同一个引用 str。//若定义另外一个str2赋值给“李四”,这个操作指令肯定就不同了。5 astore_1//访问的是System类的静态属性out,我们知道它的类型是PrintStream6 getstatic #4 <java/lang/System.out>//将out这个引用加载到操作数栈栈顶,以便后边操作它9 aload_1//调用out的实例方法 println ,用于打印结果到控制台
10 invokevirtual #5 <java/io/PrintStream.println>
//main方法返回值为void
13 return

这里就不用再多余解释了吧,可以从字节码指令中看到,后边的 str="李四" 被编译器执行了。

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

你还在为怎么查看字节码指令而担忧吗?相关推荐

  1. JVM笔记:Java虚拟机的字节码指令详解

    1.字节码 Java能发展到现在,其"一次编译,多处运行"的功能功不可没,这里最主要的功劳就是JVM和字节码了,在不同平台和操作系统上根据JVM规范的定制JVM可以运行相同字节码( ...

  2. ASM-使用ASM Bytecode Outline插件查看字节码

    使用ASM Bytecode Outline插件查看字节码 使用ASM Bytecode Outline插件查看字节码 前言 Intellij IDEA使用 Android Studio 使用 存在的 ...

  3. Java以及IDEA下查看字节码的五种方法

    1.最本质的是cmd下 #javap -v 类名 说明:这是最原始的方法,效率低下,强烈不推荐使用. 2.查看字节码的方法idea可以集成命令行使用javap 注意需要在class文件目录下,不要在j ...

  4. java虚拟机编译_[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式...

    前言简介 前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明 想要了解JVM的运行机制,以及如何优化你的代码,你还需要了解一下,java编译器到底是如何编译 ...

  5. Java字节码指令简介

    本文是<深入理解Java虚拟机>中第六章的读书笔记. 1.概述 在Class文件中,Java方法里的方法体,也就是代表着一个Java源码程序中程序的部分存储在方法表集合的Code属性中.存 ...

  6. idea如何反编译字节码指令_美团点评:Java字节码增强技术,线上问题诊断利器...

    作者简介:泽恩,美团到店住宿业务研发团队工程师.文章转载于公众号:美团技术团队 1. 字节码 1.1 什么是字节码? Java之所以可以"一次编译,到处运行",一是因为JVM针对各 ...

  7. 【JVM源码解析】模板解释器解释执行Java字节码指令(上)

    本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...

  8. 【Hotspot】 执行字节码指令流程(1):堆栈的创建以及Java函数调用

    什么是基于栈的虚拟机: 关于JVM中调用Java函数需要了解一下基于栈的虚拟机: 栈是在内存中单独维护的一个结构,栈中有局部变量表,执行操作比如a+b需要调用将两个变量加载到操作数栈中,相加后放到栈顶 ...

  9. 字节码文件及字节码指令

    我记得开始学习Java的第一堂课时,我的大学老师是这样说的,Java号称是"一次编写,到处运行",为什么有底气这样说,是因为Java程序并不是直接运行在操作系统上的,它通过不同操作 ...

最新文章

  1. 【神经网络】(18) EfficientNetV2 代码复现,网络解析,附Tensorflow完整代码
  2. Adam又要“退休”了?耶鲁大学团队提出AdaBelief,却引来网友质疑
  3. 裴健:搜索皆智能,智能皆搜索
  4. JAVA Bean和XML之间的相互转换 - XStream简单入门
  5. 2022年全球及中国盐酸异丙肾上腺素行业投资风险与运营盈利分析报告
  6. apt-get remove 与 apt-get purge 区别(删除包、卸载包)
  7. 应用程序利用ADO对象访问数据库
  8. 优酷《追光吧!》正式开播 风度、实力成关键词
  9. 字节跳动副总裁喊话腾讯:停止无理由封杀飞书;Git服务器配置错误导致日产汽车源码泄露;Linux5.10.5 发布
  10. Matplotlib——基本用法
  11. 个人网络安全从业心得
  12. 关于用友华表Cell插件代码
  13. android+excel软件,Android版Office办公软件Excel应用
  14. java中文转英文_中文转换为英文
  15. pro android python with sl4a,Pro Android Python with SL4A
  16. C#控制定位Word光标移动到任意行或者最后一行,取得光标位置等操作
  17. 嵌入式软件工程师笔试面试指南-网络编程
  18. 02.python求和
  19. Linux 链路聚合之bond和team
  20. 2020-2021 年度广东省职业院校技能大赛网络搭建与应用竞赛

热门文章

  1. mysql种default约束的语句_sql语句大全之SQL DEFAULT 约束
  2. 最大流 ---- 最大不相交路径数 ---- P2766 最长不下降子序列问题(网络流24题)
  3. 树莓派系统安装_树莓派系统安装
  4. angular select设置默认选中_改进 Angular + Jest 项目中组件测试的调试
  5. 【学习笔记】超简单的多项式快速幂
  6. 算法_贪心 刷题总结
  7. 计算机图形学直线扫描转论文,计算机图形学实验报告-实验1直线段扫描转换.doc...
  8. 在java中建个jsp文件_第一个jsp页面
  9. mysql 触发器编程_【mysql的编程专题】触发器
  10. 点击展开 表格_Excel里面如何设置默认的表格和透视表样式