原标题:大话+图说:Java 汇编指令——只为让你懂

前言

随着Android开发技术不断被推到新的高度,对于Android程序员来讲越来越需要具备一些对深入的基础性的技术的理解,比如说Java汇编指令。不然,可能很难深入理解一些时下的新框架、新技术,盲目一味追新也会越来越感乏力。

本文既不求照本宣科,亦不求炫技或著文立说,仅力图以最简明、最形象生动的方式,结合例子与实战,让小白也能搞懂这门看似复杂的技术概念。

单刀直入

闲言碎语不要讲,先表一表,什么是Java汇编指令?简而言之,Java汇编指令就是Java虚拟机能够听得懂、可执行的指令,也可以说是Java代码的最小执行单元。有点Java基础的人一定都知道,javac命令会将Java源文件编译成字节码文件,也就是.class文件,里面就包含了大量的汇编指令。因此可以将javac命令理解为一个翻译命令,将源文件翻译成虚拟机可以执行的指令。那么最直观的探究方法莫过于直接对比一下翻译前后的内容。具体如何对比呢?就不得不用到Java为我们一直默默提供的一项利器,javap命令,它可以解析字节码,将字节码内部逻辑以可读的方式呈现出来。为了紧贴实战,我们直接在新建的Android工程里,写这样一个Activity类,里面包含几个由简单到复杂的方法和一个名为name的字段:

如图,以上方法,复杂度由低到高依次为:getName

cd到这个路径下,运行万能的javap命令:javap -v -p MainActivity,就可以观看到翻译版的Java字节码的胴体了!这里的-v意思是啰嗦模式,会输出全面的字节码信息,而-p是指涵盖所有成员。原字节码信息输出内容较多,基于本文的目标,取其一方法的内容,整理如下图:方法1,getName():

这个getName的方法应该是再简单不过的Java代码,翻译成字节码后也变成了三行,我们先来简单推理一下:第一句,aload_0不知所云,索性略过;第二行,getfield应该可以读懂,后面这个#2似乎是他的参数(实际上是对常量池的引用),//后面注释的内容是javap给我们加上的,意思应该是#2的指向是"Field name:Ljava/lang/String;"这个内容。所以getfield这一行就是取出name这个字段喽,so easy。areturn肯定就是return的意思,a的含义也先略过不表。总之就是取出name字段然后return喽。

那么现在的问题就是aload_0是什么意思了,看似多余,但仔细思考一下,似乎之前给getfield指令传入了“Field name:Ljava/lang/String;”这样一个并不完整的参数,其后半部分的“Ljava/lang/String;”仅仅表示这个name字段的类型是String,也就是说,整个参数里没有说是取的谁的name字段啊!究竟是get谁的feild呢?

由此可以想到:aload操作一定是在为getfield指令准备了一个主体。

实际上,再结合下面的局部变量表,aload_0中的0正是局部变量表里的Slot 0的含义。意思是将局部变量表里的Slot 0的东西压入操作数栈,这个Slot 0里的东西name正是this,也就是MainActivity的实例,即getfield的主体。

大戏上演

好了,对于小白同学有些陌生的概念来了,啥是操作数栈?啥是局部变量表?其实这两个东西理解好了,关于虚拟机指令就懂了一大半了。那么,不妨删繁就简,由易入难,先讲一个这样的故事,故事起名叫:

Java方法之创世纪

话说Jvm大帝是神之旨意的履行者(Jvm大帝就是虚拟机,神就是开发者,神之旨意是开发者写好并编译后的字节码...),当Jvm大帝带领Java世界运行进入了一个新的方法后,会为这个方法在栈内存大陆上创造两个重要的领域:局部变量表和操作数栈。

要有栈。要有表。神说。

依照神之旨意,jvm大帝创造的局部变量表里一般会包含this指针(针对实例方法,静态方法当然无此)、方法的所有传入参数和方法中所开辟的本地变量。

那么操作数栈是干嘛用的呢?

我们再引入另外一个比喻,如果把运行Java方法理解为拍戏,那么局部变量表里的各个局部变量就是这部戏的核心主角,或者说领衔主演,而操作数栈正是这部戏的舞台。所谓操作数栈搭台,局部变量唱戏,是也。那么aload0就是告诉Jvm导演(大帝已沦落为导演),请0号演员this同志登台(压栈),演后边的本子。当然了,这个比喻并不完全恰当,因为操作数栈并不是“舞台”的结构,而是栈的结构。但是这个比喻可以很好地说明局部变量表和操作数栈之间的关系,以及aload0的作用。

下面我们用一张图来演示一下getName这个小剧本桥段所导演的故事:

好吧这部剧虽然短的可怜,但已经基本把指令、操作数栈和局部变量表三者的关系演绎了出来。值得注意的是,getfield这条指令对操作数栈进行了复合操作,其流程可以示意如下图:

后面我们将要接触到的许多指令都如此,指令内部执行了弹出—>处理—>压回的流程。下面我们就来分析一个相对复杂一点的方法,setName(String),如下图:

这里我们看到,变化主要有,指令多了一行,多进行了一次aload,getfield变成了putfield,areturn变成了return,仅此而已。另外领衔主演也就是局部变量表里多了一位,也就是方法的传入参数name字符串对象了。其情节如下:

这里,putfield只弹出栈内的操作数,而没有向操作数栈压回任何数据,而且执行putfield之前,栈内元素的位置也必须符合“值在上,主体在下”要求。而最后的return仅表示方法结束,而不会像areturn一样返回栈顶元素。这也印证了setName(String)方法没有返回参数。

融会贯通

相信有了以上的讲解,大家对指令、操作数栈、局部变量表三者的运作关系有了一定认识,为了后边能够分析更复杂的方法,这里必须概括性地讲解一下更多的Jvm汇编指令。虽然Jvm汇编指令非常多,但其实常用的不外乎几个类别,先从这几个常用类别入手理解,便可渐入佳境。关于Java汇编指令的分类,可以从两个维度进行:一是指令的功能,二是指令操作的数据类型。我们先从功能说起,指令主要可以分为如下几类:

存储和加载类指令:主要包括load系列指令、store系列指令和ldc、push系列指令,主要用于在局部变量表、操作数栈和常量池三者之间进行数据调度;(关于常量池前面没有特别讲解,这个也很简单,顾名思义,就是这个池子里放着各种常量,好比片场的道具库)

对象操作指令(创建与读写访问):比如我们刚刚的putfield和getfield就属于读写访问的指令,此外还有putstatic/getstatic,还有new系列指令,以及instanceof等指令。

操作数栈管理指令:如pop和dup,他们只对操作数栈进行操作。

类型转换指令和运算指令:如add/div/l2i等系列指令,实际上这类指令一般也只对操作数栈进行操作。

控制跳转指令:这类里包含常用的if系列指令以及goto类指令。

方法调用和返回指令:主要包括invoke系列指令和return系列指令。这类指令也意味这一个方法空间的开辟和结束,即invoke会唤醒一个新的java方法小宇宙(新的栈和局部变量表),而return则意味着这个宇宙的结束回收。

如下图,展示了各类指令的作用:

再从另外一个维度,即指令操作的数据类型来讲:指令开头或尾部的一些字母,就往往表明了它所能操作的数据类型:

a对应对象,表示指令操作对象性数据,比如aload和astore、areturn等等。i对应整形。也就有iload,istore等i系列指令。f对应浮点型。l对应long,b对应byte,d对应double,c对应char。另外地,ia对应int array,aa对应object array,da对应double array。不在一一赘述。

了解了以上内容,我们再去看最后几个方法,应该就会容易理解很多了。下面我们就直捣黄龙gotoBrowser这个方法:

这个过程简单解读如下:

new一个Intent对象(在堆内存中开辟空间),并将其引用入栈;

dup复制栈顶的刚刚放入的引用,再次压栈,这时栈里有两个重复的内容,深度为2;

从常量池取出“android.intent.action.View”这个字符串(对象引用)压栈,此时栈深度为3;

弹出栈顶的两个对象,调用弹出的第二个对象的 方法,栈深度为1;

将此时栈顶引用弹出并存储到局部变量中(slot 2),此时栈就清空了,深度0;

后面的就相对简单了,读者可以尝试自己解读。再看这个包含if跳转的方法staticMethod:

如上图,图中已经说明的比较全面了,不再赘述。值得一提的是,Java的这种基于栈结构的汇编指令,在设计上有一种非常简洁的美感,指令与指令之间并没有较重的依赖,每条指令仅仅与操作数栈等领域内的数据发生关系,充满着某种平衡与秩序感。因此也必须注意,几乎每条指令的运行都有其前提,比如在invokevirtual或invokespecial指令执行前,必须保证操作数栈内提前按顺序压入好所需的操作数,否则就会发生问题。关于最复杂的onCreate方法,就不再啰嗦解读了,读者可以前往我的github上的对应demo repo,进入tutorial分支,拉取源码和教程资源,或者自己写demo体验这一完整过程。地址:https://github.com/BryanSharp/hiBeaverDemo

后话

关于实战,一是可以学习使用强大开源工具ASM.jar;二是,特别对于Android同学,可以参考本人的另一篇文章:Android字节码修改神器HiBeaver:黑掉你的SDK(https://segmentfault.com/a/1190000008491823)以及一次Android字节码插桩实战(https://segmentfault.com/a/1190000008658815),利用hibeaver这个助手,开发者可以非常灵活地对字节码进行修改,插入指令,hook代码,甚至建立一些简单的AOP框架,对于Java字节码学习大有裨益。hibeaver完全开源,github项目地址:https://github.com/BryanSharp/hibeaver

祝玩的愉快!本文如有不妥之处,欢迎交流指正。

另外,本文为了尽可能地简明生动、直入核心,简化了很多概念和细节,读者须知实际情况的更为复杂。但相信在理解了本文以后,就可以抓住Java汇编指令的核心理念,也就算扣开虚拟机学习的大门并可以开始读书精进了。下面盗图一张(后有出处),可作拓展:

链接:http://blog.csdn.net/luanlouis/article/details/39892027返回搜狐,查看更多

责任编辑:

java 汇编_大话+图说:Java 汇编指令——只为让你懂相关推荐

  1. java 虚拟机_浅谈Java虚拟机内存区

    1. Java 虚拟机内存区概述 我们在编写程序时,经常会遇到OOM(out of Memory)以及内存泄漏等问题.为了避免出现这些问题,我们首先必须对JVM的内存划分有个具体的认识.JVM将内存主 ...

  2. Java继承_深入理解Java继承、封装、多态的实现原理

    点击关注上方"Java技术江湖",设为"置顶或星标",第一时间送达技术干货. 作者:黄小斜 文章来源:微信公众号[Java技术江湖] 目录 从JVM结构开始谈多 ...

  3. java 抛出异常_我的Java Web之路51 - Java异常基础

    本系列文章旨在记录和总结自己在Java Web开发之路上的知识点.经验.问题和思考,希望能帮助更多(Java)码农和想成为(Java)码农的人. 目录 介绍 异常的本质 Java异常的设计 Java异 ...

  4. groovy 使用java类_深入学习java中的Groovy 和 Scala 类

    前言 Java 传承的是平台,而不是语言.有超过 200 种语言可以在 JVM 上运行,它们之中不可避免地会有一种语言最终将取代 Java 语言,成为编写 JVM 程序的最佳方式.本系列将探讨三种下一 ...

  5. java 枚举_深入理解Java枚举

    所有知识体系文章,[GitHub](https://github.com/Ziphtracks/JavaLearningmanual)已收录,欢迎Star!再次感谢,愿你早日进入大厂! https:/ ...

  6. java翻译程序_有没有java代码翻译软件?

    展开全部 没有翻译软件,但是能32313133353236313431303231363533e78988e69d8331333365646333编写翻译程序. java 代码翻译实例: 1.输入一个 ...

  7. 《跟旺旺老师学Java》_第一章Java简介与JDK部署_第一部分Java是什么

    第一章:Java简介与JDK部署 第一部分:java是什么 "是白云,是瀑布,是海滩,不---,都不是,这是太原大理石二厂生产的景云牌大理石"看到这个标题,脑海中莫名的闪过这句小时 ...

  8. opencv java 摄像头_使用OpenCV Java创建Windows摄像头扫码程序

    OpenCV提供了一些基本的Webcam控制接口.用OpenCV C/C++或者Python,可以在任意平台快速创建一个摄像头预览应用.然而使用Java,情况就复杂的多,因为OpenCV Java并没 ...

  9. java横线_知识点:java一些方法会有横线?以Date 过期方法为例

    原因:他们的开发者在升级方法后,添加了@Deprecated注释, 目的是为了提醒我们,这个方法现在已经有新的方法了,不建议继续使用! 比如: JAVA中Date的tolocalstring为什么不建 ...

最新文章

  1. 数组拷贝问题,实际上是指针指向发生变化
  2. Oracle.DataAccess.Client.OracleCommand”的类型初始值设定项引发异常
  3. CTFshow php特性 web129
  4. 用计算机控制检测实时温度,温度实时测量及控制系统.doc
  5. IC设计Linux设置文件夹颜色,IC设计中常用的Linux命令
  6. kotlin学习笔记——委托属性
  7. Docker 安装zookeeper
  8. java多叉树的遍历
  9. mysql function_MySQL基础函数——数学函数详解
  10. Err:ClassNotFoundException: org.apache.tomcat.util.log.SystemLogHandler
  11. 【python】集合的定义与操作
  12. JAVA 中序遍历的非递归栈实现
  13. 结对编程之求首尾相连数组中最大子数组的和
  14. Scikit-Learn (浅谈PCA降维算法)
  15. 关于中缀表达式的计算 和算符优先算法
  16. Linux系统搭建私有CA证书服务器
  17. 游轮旅游是三亚旅游的未来
  18. 魔兽怀旧服务器位置,《魔兽世界》怀旧服稀有狼位置坐标大全
  19. 这一份最全的TCP总结,请务必收下
  20. 常用工具类之jwt的学习使用

热门文章

  1. 手推SVM及SMO算法实现
  2. Zhong__MySQL笔记
  3. Zhong__Python lambda()
  4. excel高级筛选怎么用_表格技巧—Excel高级筛选怎么用
  5. 远程桌面控制软件 Splashtop 新增本地部署版产品 On-Prem
  6. carbon安装win7 thinkpad x1_ThinkPad X1 Carbon笔记本U盘重装系统win7教程
  7. Redis:缩容、扩容、渐进式rehash
  8. AI文娱独角兽Video++极链科技完成C1轮,5个月融资10.7亿元
  9. word2计算机fx公式,Word的fx公式怎么使用说明
  10. AMBER:对单个复合物进行分子动力学模拟的python包(resp计算电荷及gpu加速版本)