java字节码_掌握Java字节码

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

java字节码

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

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

相关文章

自由空间激光通信

一、空间激光通信系统 空间激光通信系统是指以激光光波作为载波&#xff0c;大气作为传输介质的光通信系统。自由空间激光通信结合了光纤通信与微波通信的优点&#xff0c;既具有大通信容量、高速传输的优点&#xff0c;又不需要铺设光纤&#xff0c;因此各技术强国在空间激光…

icom对讲机写频线定义_小米对讲机Lite体验:小米全新5公里无网络免费通话神器...

沟通&#xff0c;或者可以简单地把它说成是一种信息的传递。它对于世间的生灵来说&#xff0c;都很重要&#xff01;尤其是对于人类&#xff0c;更为重要&#xff01;由古至今&#xff0c;人们为了可以更好地进行信息的传递&#xff0c;想出了各种各样的办法&#xff0c;比如古…

Spring Boot应用程序的“本地服务”

我有机会尝试使用Knative的 Serving功能来部署Spring Boot应用程序&#xff0c;而这篇文章只是记录了示例和我采用的方法。 我对Knative的内部知识还不够了解&#xff0c;无法就此方法是否比基于部署 服务 基于入口的方法更好。 一项很棒的功能是Knative Serving中的自动缩放…

一文读懂:常见低通、高通、带通三种滤波器的工作原理

滤波器 滤波器是对波进行过滤的器件&#xff0c;是一种让某一频带内信号通过&#xff0c;同时又阻止这一频带外信号通过的电路。 滤波器主要有低通滤波器、高通滤波器和带通滤波器三种&#xff0c;按照电路工作原理又可分为无源和有源滤波器两大类。今天&#xff0c;小编主要…

红米ac2100breed看模块_骁龙865加持,红米K30S至尊纪念版11日再开售

红米K30S至尊纪念版在11月1日首销&#xff0c;仅用了1分钟便售出10万台&#xff0c;热度颇高。11月11日0点&#xff0c;红米K30S至尊纪念版将再度开售&#xff0c;售价2299元起。以2299元起步的价格&#xff0c;红米K30S至尊纪念版配备了高通骁龙865处理器&#xff0c;8GB LPDD…

常用求和公式和级数

1. 多项式级数 2. 指数级数 3. Harmonic 级数 4. 其他 常见的级数查询表 https://blog.csdn.net/algzjh/article/details/82533996 https://www.zhihu.com/topic/19998598/hot

个盘子的汉诺塔需要移动几步_看漫画学C++039:递归解汉诺塔

点击蓝字 关注我们本话内容请输入相传在古印度圣庙中&#xff0c;有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上&#xff0c;有三根杆(编号A、B、C)&#xff0c;在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标&#xff1a;把A杆上的金盘全部移到…

八位图 16位图_了解位图

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

调制深度,峰均值功率比,脉冲整形等因素对于调制损耗的影响...

我们知道光发射机中一个非常重要的参数是发射功率。如果光发射机处于无源光网络&#xff08;PON&#xff09;&#xff0c;没有后续放大器的情况&#xff0c;发射功率很大程度上决定了发射机可以传输的距离 &#xff08;功率预算&#xff0c;power budget&#xff09;&#xff1…

移动流量转赠给好友_私域流量的五大认知误区

各大品牌主对于私域流量存在的认知误区 &#xff0c;你是否也这样认为&#xff1f;目前看起来&#xff0c;私域流量好像只要前期投入&#xff0c;后期就可以为自身带来源源不断的流量&#xff0c;但是私域流量真的像看起来那么美吗&#xff1f;其实不是的&#xff0c;我们对于私…

MATLAB 显示输出数据的三种方式

1. 改变数据格式 当数据重复再命令行窗口时&#xff0c;整数以整形形式显示&#xff0c;其他值将以默认格式显示。MATLAB的默认格式是精确到小数点后4位。如果一个数太大或太小&#xff0c;那么将会以科学记数法的形式显示。比如&#xff1a; >> x100.11x 100.1100>…

Matlab 语句

1. 显示输出数据的三种方式 1.1 改变数据格式 当数据重复再命令行窗口时&#xff0c;整数以整形形式显示&#xff0c;其他值将以默认格式显示。MATLAB的默认格式是精确到小数点后4位。如果一个数太大或太小&#xff0c;那么将会以科学记数法的形式显示。比如&#xff1a; &g…

Probability, Matringale,Markov Chain, MCMC

一、基本知识 1. 条件概率 条件概率是指在某件事情已经发生的前提下&#xff0c;另一件事情在此基础上发生的概率&#xff0c;举例来说P(A丨B)表示B发生的基础上&#xff0c;A也发生的概率&#xff0c;基本公式为&#xff1a; 2. 条件期望 在上述概率下的期望我们称之为条…

html 按钮 按下 状态_科普|你身边的手动火灾报警按钮,您了解吗?

手动火灾报警按钮手动火灾报警按钮&#xff0c;是火灾报警系统中的一个设备类型&#xff0c;当建筑发生火灾时在火灾探测器没有探测到火灾的时候人员手动按下手动火灾报警按钮&#xff0c;报告火灾信号&#xff0c;向建筑所属消防控制室报火警。正常情况下当手动火灾报警按钮报…

马尔可夫蒙特卡罗 MCMC 原理及经典实现

我们在做机器学习、深度学习或自然语言处理等项目时&#xff0c;经常采用什么方法采样呢&#xff1f;大家马上会想到吉布斯 Gibbs 采样&#xff0c;今天我们来分享一种比较实用的采样方法&#xff1a;马尔可夫蒙特卡罗方法&#xff0c;吉布斯采样是其中的一种。 Markov chain …

les物流执行系统_【精益运营】立足智慧物流 推进仓储智能化稳步升级

近年来&#xff0c;“智能制造”成为制造行业的热门词汇&#xff0c;也成为引领物流行业发展的风向标。今年在两会中&#xff0c;“智能”一词首次写入报告&#xff0c;为传统行业改造升级、实现协同共享带来了新的发展机遇。早在2017年12月&#xff0c;根据公司对VMI业务整合工…

增益比值 dB 以及 dBw-dBmv 等之详解

dB 分贝&#xff08;工程应用&#xff09;&#xff0c;dB&#xff08;Decibel&#xff0c;分贝&#xff09;是一个纯计数单位&#xff0c;本意是表示两个量的比值大小&#xff0c;没有单位。在工程应用中经常看到貌似不同的定义方式&#xff08;仅仅是看上去不同&#xff09;。…

带有Spring Boot 2支持的Apache Camel 2.22发布

今天&#xff0c;我们发布了最新的Apache Camel 2.22.0版本 &#xff0c;这是第一个正式完全支持Spring Boot 2的版本。这是一项重大的工作&#xff0c;因为针对像Apache Camel这样的大型框架将Spring Boot v1升级到v2付出了很多努力–感谢骆驼队和他们的贡献。 对Spring Boot …

java 分布式事务_Java核心知识 Spring原理十五 JPA 原理

1. 事务事务是计算机应用中不可或缺的组件模型&#xff0c;它保证了用户操作的原子性 ( Atomicity )、一致性( Consistency )、隔离性 ( Isolation ) 和持久性 ( Durabilily )。2. 本地事务紧密依赖于底层资源管理器(例如数据库连接 )&#xff0c;事务处理局限在当前事务资源内…

Latex中定理、引理、证明、假设、空白行的编写

1. 引理 \newtheorem{lemma}{Lemma}[section] \begin{lemma} \label{lemma1}\end{lemma}编译效果&#xff1a; 2. 定理 \newtheorem{thm}{\bf Theorem}[section] \begin{thm}\label{thm1} Suppose system (\ref{l1}) satisfies Assumption (\ref{mim1}), the closed-loop s…