掌握Java字节码

嘿! Happy Advent:D我是ZeroTurnaround的技术布道者Simon Maple( @sjmaple) 。 您知道, JRebel伙计们! 由于编写了类似于JRebel的产品,该产品与字节码进行交互的结果比您想像的更多,因此,我们希望与他人分享很多有关它的知识。

让我们从头开始……Java是一种旨在在虚拟机上运行的语言,因此只需要编译一次就可以在任何地方运行(是的,是的,一次编写,可以在任何地方进行测试)。 结果,您安装到系统上的JVM将是本机的,从而允许在其上运行的代码与平台无关。 Java字节码是您作为源编写的Java代码的中间表示,并且是编译代码的结果。 因此,您的类文件是字节码。

更简洁地说,Java字节码是Java虚拟机使用的代码集,该代码集在运行时被JIT编译为本机代码。

您曾经玩过汇编程序或机器代码吗? 从某种意义上说,字节码有点类似,但是行业内很多人并没有那么多地使用它,更多是出于缺乏必要性。 但是,了解正在发生的事情非常重要,如果您想让酒吧里的某个人望而却步,这很有用。

首先,让我们看一些字节码基础知识。 我们将首先使用表达式“ 1 + 2”,并查看如何将其作为Java字节码执行。 1 + 2可以用反向波兰语表示为1 2 +。 为什么? 好吧,当我们将其放在堆栈上时,一切都变得清晰了……

1个

好的,在字节码中,我们实际上会看到操作码(iconst_1和iconst_2)和一条指令(iadd),而不是推和加,但流程是相同的。 实际指令的长度为一个字节,因此为字节码。 结果有256种可能的操作码,但仅使用了200种左右。 操作码的前缀是类型,后跟操作名称。 因此,我们之前在iconst和iadd上看到的是整数类型的常量和整数类型的加法指令。

这一切都很好,但是如何读取类文件。 通常,在打开的类文件中,通常在选择的编辑器中看到的只是一堆笑脸以及一些正方形,圆点和其他奇怪的字符,对吗? 答案是在Javap中,这是您随JDK实际获得的代码实用程序。 让我们看一个代码示例,看看运行中的javap。

public class Main {public static void main(String[] args){MovingAverage app = new MovingAverage();}}

将此类编译为Main.class文件后,我们可以使用以下命令提取字节码:javap -c Main

Compiled from "Main.java"public class algo.Main {public algo.Main();Code:0: aload_01: invokespecial #14: return
// Method java/lang/Object."<init>":()V
public static void main(java.lang.String[]);Code:0: new           #23: dup4: invokespecial #37: astore_18: return 
}

我们可以立即在字节码中看到我们的默认构造函数和main方法。 顺便说一下,这就是Java为无构造函数的类提供默认构造函数的方式! 构造函数中的字节码只是对super()的调用,而我们的main方法创建了MovingAverage的新实例并返回。 #n字符实际上是指可以使用-verbose参数查看的常量,如下所示:javap -c -verbose Main。 返回内容的有趣部分如下所示:

public class algo.MainSourceFile: "Main.java"minor version: 0major version: 51flags: ACC_PUBLIC, ACC_SUPERConstant pool:#1 = Methodref    #5.#21         //  java/lang/Object."<init>":()V#2 = Class        #22            //  algo/MovingAverage#3 = Methodref    #2.#21         //  algo/MovingAverage."<init>":()V#4 = Class        #23            //  algo/Main#5 = Class        #24            //  java/lang/Object

现在,我们可以将指令与常量进行匹配,并且可以将实际发生的事情拼凑起来要容易得多。 关于上面的示例,您还有什么困扰吗? 没有? 那每条指令前面的数字呢?

0: new           #23: dup4: invokespecial #37: astore_18: return

现在真的很烦你,对吧? :)如果将这个方法体可视化为数组,这就是我们得到的:

2

请注意,每条指令都有一个十六进制表示,因此使用它我们实际上会看到以下内容:

3

如果在十六进制编辑器中打开它,我们实际上可以在类文件中看到它:

4

实际上,我们可以在HEX编辑器中更改字节码,但是老实说,这不是您真正想做的事情,尤其是在强制性酒吧旅行之后的星期五下午。 更好的方法是使用ASM或javassist。

让我们从基本示例继续,添加一些存储状态并直接与堆栈交互的局部变量。 查看以下代码:

public static void main(String[] args) {MovingAverage ma = new MovingAverage();int num1 = 1;int num2 = 2;ma.submit(num1);ma.submit(num2);double avg = ma.getAvg();}

让我们看看这次在字节码中得到了什么:

Code:
0: new  #2    // class algo/MovingAverage3: dup4: invokespecial #3  // Method algo/MovingAverage."<init>":()V7: astore_18: iconst_19: istore_210: iconst_211: istore_312: aload_113: iload_214: i2d15: invokevirtual #4        // Method algo/MovingAverage.submit:(D)V18: aload_119: iload_320: i2d21: invokevirtual #4        // Method algo/MovingAverage.submit:(D)V24: aload_125: invokevirtual #5        // Method algo/MovingAverage.getAvg:()D28: dstore     4LocalVariableTable:Start  Length  Slot  Name   Signature0       31         0    args   [Ljava/lang/String;8       23        1      ma     Lalgo/MovingAverage;10      21         2     num1   I12       19         3      num2   I30       1        4    avg     D

这看起来更加有趣……我们可以看到,我们创建了一个类型为MovingAverage的对象,该对象通过astore_1指令(1是LocalVariableTable中的插槽号)存储在本地变量ma中。 指令iconst_1和iconst_2在那里将常数1和2加载到堆栈中,并分别通过指令istore_2和istore_3将它们存储在LocalVariableTable插槽2和3中。 一条加载指令将一个局部变量压入堆栈,一条存储指令从堆栈中弹出下一项并将其存储在LocalVariableTable中。 重要的是要认识到,当使用存储指令时,该项目将从堆栈中取出,如果要再次使用它,则需要加载它。

执行流程如何? 我们所看到的只是从一行到下一行的简单进展。 我希望看到一些BASIC风格的GOTO 10混在一起! 让我们再举一个例子:

MovingAverage ma = new MovingAverage();for (int number : numbers) {ma.submit(number);}

在这种情况下,当我们遍历for循环时,执行流程将跳很多次。 假定数字变量是同一类中的静态字段,则此字节码如下所示:

0: new #2 // class algo/MovingAverage3: dup4: invokespecial #3 // Method algo/MovingAverage."<init>":()V7: astore_18: getstatic #4 // Field numbers:[I11: astore_212: aload_213: arraylength14: istore_315: iconst_016: istore 418: iload 420: iload_321: if_icmpge 4324: aload_225: iload 427: iaload28: istore 530: aload_131: iload 533: i2d34: invokevirtual #5 // Method algo/MovingAverage.submit:(D)V37: iinc 4, 140: goto 1843: returnLocalVariableTable:Start  Length  Slot  Name   Signature30       7         5    number I 12       31        2    arr$     [I15       28        3    len     $I 18       25         4     i$      I0       49         0     args  [Ljava/lang/String;8       41         1    ma     Lalgo/MovingAverage; 48      1         2    avg    D

从位置8到17的指令用于设置循环。 SourceVariable表中有三个在源代码中并未真正提及的变量arr $,len $和i $。 这些是循环变量。 arr $存储number字段的参考值,从中得出循环长度len $。 i $是循环计数器,由iinc指令递增。

首先,我们需要测试我们的循环表达式,该表达式由比较指令执行:

18: iload 420: iload_321: if_icmpge 43

我们将4和4加载到堆栈上,分别是循环计数器和循环长度。 我们正在检查ID i $大于或等于len $。 如果是,则跳至语句43,否则继续进行。 然后,我们可以在循环中执行逻辑,最后,我们增加计数器并跳回到检查语句18的循环条件的代码。

37: iinc       4, 1       // increment i$40: goto       18         // jump back to the beginning of the loop

可以在字节码中使用一堆算术操作码和类型命令组合,包括以下内容:

5

以及许多类型转换操作码,这些类型转换操作码在为long类型的变量分配一个整数时很重要。

6

在我们的珍贵示例中,我们将一个整数传递给一个采用双精度值的submit方法。 Java语法为我们完成了此操作,但是在字节码中,您将看到使用了i2d操作码:

31: iload 533: i2d
34: invokevirtual #5 // Method algo/MovingAverage.submit:(D)V

因此,您已经做到了这一点。 做得好,您已经喝咖啡了! 了解这些内容是否真的有用还是仅仅是怪胎? 好吧,两者都有! 首先,您现在可以告诉您的朋友,您是可以处理字节码的JVM,其次,您可以更好地了解编写字节码时的操作。 例如,使用ObjectWeb ASM(这是使用最广泛的字节码操作工具之一)时,您会发现自己正在构造指令,并且这些知识将证明是无价的!

如果您发现这有趣并且想了解更多,请查看ZeroTurnaround的JRebel产品负责人Anton Arhipov的免费Mastering Java Bytecode报告。 (JRebel使用javassist,我们学习了很多有趣的东西,并且可以与Java字节码进行交互!)该报告更深入地介绍了如何使用ASM。

谢谢阅读! 让我知道你的想法! ( @sjmaple )

参考:在Java Advent Calendar博客上,从我们的JCG合作伙伴 Attila Mihaly Balazs 掌握Java字节码 。

翻译自: https://www.javacodegeeks.com/2013/12/mastering-java-bytecode.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

敲代码括号技巧_理解代码块概念,养成良好编程习惯 | 亲子课堂 第 3 课

亲子课堂关卡解析 / 英语教学 / 编程讲解 做亲子编程教育的好帮手&#xff01; 每周二、四定期更新 地牢面向真正0编程基础的孩子们&#xff0c;关卡被设计成迷宫的形式&#xff0c;引导孩子们使用编程思维解决问题。以循序渐进的方式&#xff0c;让大家理解掌握几个Pyth…

在struts2中push方法的使用_电脑使用中怎么截屏的几种方法

电脑在日常工作中经常需要用到截屏的操作&#xff0c;为了截取画面提供证明或者说明&#xff0c;像我就经常需要用到&#xff0c;当然我在写文章的时候更是需要用到&#xff0c;来配合文字的描述&#xff0c;使大家能更直观更容易的去操作&#xff0c;以达到快速解决电脑问题的…

卸载WPS后,原office出现各种问题,报错,图标混乱

1.运行环境win7专业版64位操作系统&#xff0c;之前电脑上装了WPS和office2013&#xff0c;后来卸载了WPS&#xff0c;导致office图标显示不正常&#xff08;因为WPS与office有很多冲突的地方&#xff0c;卸载的时候会影响到注册表&#xff0c;导致office的注册表损坏&#xff…

C#中的CultureInfo类

CultureInfo类位于System.Globalization命名空间内&#xff0c;这个类和命名空间许多人都不是很熟悉&#xff0c;实际我们在写程序写都经常间接性的接触这个类&#xff0c;当进行数字&#xff0c;日期时间&#xff0c;字符串匹配时&#xff0c;都会进行CultureInfo的操作&#…

clistctrl控件最后插入在后面_老板让我把图片放到Excel表格中,批量插入效率高...

私信回复关键词【福利】&#xff0c;获取丰富办公资源&#xff01;助你高效办公早下班&#xff01;大家好&#xff0c;我是懂点 Excel 的小E~初入「江湖」&#xff0c;还请大家多多关照&#xff01;今天我们来学学 Excel 图片的 6 个小技巧&#xff0c;满满都是干货&#xff0c…

python天气预报的功能介绍_python实现智能语音天气预报

本系统主要包括四个函数&#xff1a; 1、获取天气数据 1、输入要查询天气的城市 2、利用urllib模块向中华万年历天气api接口请求天气数据 3、利用gzip解压获取到的数据&#xff0c;并编码utf-8 4、利用json转化成python识别的数据&#xff0c;返回为天气预报数据复杂形式的字典…

部署环境_Hyperledger Fabric Composer环境部署(一)

声明&#xff1a;本体系Hyperledger Fabric Composer相关分享都是基于MacPro进行多次亲测和体验。今天我这里主要介绍的是这些先决条件安装完成后&#xff0c;如何搭建和使用一个基础环境的问题&#xff0c;适合有一定基础的同学一起交流&#xff0c;也欢迎随时留言交流&#x…

输入参数的数目不足_sklearn.decomposition.PCA 参数速查手册

sklearn常用的API参数解析&#xff1a;sklearn.linear_model.LinearRegression调用sklearn.decomposition.PCA(n_componentsNone, copyTrue, whitenFalse, svd_solverauto, tol0.0, iterated_powerauto, random_stateNone)参数n_components释义PCA 算法中所要保留的主成分个数 …

了解位图

几周前&#xff0c;我和Alistair一起研究了用于对Neo4j数据库中节点已附加到其上的标签进行建模的代码。 这种工作方式是将32个节点ID的块表示为每个标签的32位位图 &#xff0c;其中位1表示节点具有标签&#xff0c;而0表示没有标签。 例如&#xff0c;假设我们有节点ID 0-3…

各种说明方法的答题格式_语文万能答题公式,给孩子收藏吧!

1、某句话在文中的作用1、文首&#xff1a;开篇点题&#xff1b;渲染气氛(散文)&#xff0c;埋下伏笔(记叙类文章)&#xff0c;设置悬念(小说)&#xff0c;为下文作辅垫&#xff1b;总领下文&#xff1b;2、文中&#xff1a;承上启下&#xff1b;总领下文&#xff1b;总结上文&…

pr文字转语音有插件吗_文字转语音软件深度解析

网络时代的迅猛发展&#xff0c;丰富了我们的眼见&#xff0c;便利了我们的生活&#xff0c;升华了我们的素养。那你知道近年来配音行业的迅速崛起吗&#xff1f;视频配音&#xff0c;广告配音&#xff0c;小视频配音等等&#xff0c;其实配音处处都陪伴在我们身边&#xff0c;…

装饰图案

装饰图案 我不经常使用的一种设计模式是Decorator 。 我不确定为什么这种模式不受欢迎&#xff0c;因为它很方便。 装饰器模式允许以受控方式向对象添加功能。 即使在使用静态类型的语言时&#xff0c;也可以在运行时运行&#xff01; 装饰器模式是子类的替代方法。 子类化在编…

Neural Networks

神经网络能够使用torch.nn包构建神经网络。 现在你已经对autogard有了初步的了解&#xff0c;nn基于autograd来定义模型并进行微分。一个nn.Module包含层&#xff0c;和一个forward(input)方法并返回output。 以如下分类数字图片的网络所示&#xff1a; 这是一个简单的前馈网络…

文件服务器磁盘配额管理,Windows2008 (FSRM)文件服务器资源管理器网站文件夹磁盘配额管理...

在windows server里提供了一个功能强大的管理工具&#xff0c;就是文件服务器资源管理器。简称FSRM(File Server Resource Manager)通过此工具&#xff0c;可能对网站进去配额以及相关服务。我们通过使用FSRM&#xff0c;可以为文件夹和卷设置配额&#xff0c;主动屏蔽文件&…

SpringBoot项目中,获取配置文件信息

1.在配置文件中设置信息&#xff0c;格式如下 wechat:mpAppId: wxdf2b09f280e6e6e2mpAppSecret: f924b2e9f140ac98f9cb5317a8951c71 如果是多级目录&#xff0c;则 project:url:sell: http://localhost:8080 2.获取配置文件信息&#xff08;三种方法&#xff09; 2.1Configurat…

oppo 手机侧滑快捷菜单_OPPO刚秀出卷轴屏手机,就被打了一记响亮的“耳光”

在刚刚过去的未来科技大会上&#xff0c;我国国产手机厂商 oppo可谓是出尽了风头&#xff0c;因为他们推出一款名叫“OPPO X 2021”的卷轴屏概念手机&#xff0c;并且展出了可操作的概念机实物&#xff0c;着实让所有人都惊艳了一把。因此我国的一些自媒体又嗨了&#xff0c;用…

UVA1602 Lattice Animals 搜索+剪枝

题目大意 给出一个$w\times h$的网格&#xff0c;定义一个连通块为一个元素个数为$n$的方格的集合$A,\forall x\in A, \exists y\in A$&#xff0c;使得$x,y$有一条公共边。现要求一个元素个数极多的连通块的集合$K_N$&#xff0c;使得$\forall A,B\in K_n$&#xff0c;不存在一…

python怎么打开程序管理器_Python 进程管理工具 Supervisor 使用教程

因为我的个人网站 restran.net 已经启用&#xff0c;博客园的内容已经不再更新。请访问我的个人网站获取这篇文章的最新内容&#xff0c;Python 进程管理工具 Supervisor 使用教程 Supervisor 是基于 Python 的进程管理工具&#xff0c;只能运行在 Unix-Like 的系统上&#xff…

ft服务器设置传输协议,ft服务器设置成主动模式

ft服务器设置成主动模式 内容精选换一换如果您选择使用SFS Turbo实现文件共享存储&#xff0c;此章节操作可跳过&#xff0c;您可以参见《SAP HANA用户指南》中的“格式化磁盘”章节&#xff0c;挂载Backup卷。NFS Server磁盘需要格式化&#xff0c;并挂载到相应的目录后&#…

mysql text字段导出_Mysql数据库的各种命令:

一、连接MYSQL格式&#xff1a; mysql -h主机地址 -u用户名 -p用户密码1、连接到本机上的MYSQL。 首先打开DOS窗口&#xff0c;然后进入目录mysqlbin&#xff0c;再键入命令mysql -u root -p&#xff0c;回车后提示你输密码. 注意用户名前可以有空格也可以没有空格&#xff0c;…