字节跳动_掌握Java字节码
字节跳动
嘿! 来临快乐:D我是ZeroTurnaround的技术布道者Simon Maple( @sjmaple) 。 您知道, JRebel伙计们! 由于编写了类似JRebel的产品,该产品与字节码进行交互的结果比您想像中的要多,因此我们想分享很多关于它的知识。
让我们从头开始……Java是一种旨在在虚拟机上运行的语言,因此只需要编译一次就可以在任何地方运行(是的,是的,一次编写,可以在任何地方进行测试)。 结果,您安装到系统上的JVM将是本机的,从而允许在其上运行的代码与平台无关。 Java字节码是您作为源编写的Java代码的中间表示,并且是编译代码的结果。 因此,您的类文件是字节码。
更简洁地说,Java字节码是Java虚拟机使用的代码集,该代码集在运行时被JIT编译为本机代码。
您曾经玩过汇编程序或机器代码吗? 从某种意义上说,字节码有点类似,但是行业内很多人并没有那么多地使用它,更多是出于缺乏必要性。 但是,了解正在发生的事情非常重要,如果您想让酒吧里的某个人望而却步,这很有用。
首先,让我们看一些字节码基础知识。 我们将首先使用表达式“ 1 + 2”,然后看看如何将其作为Java字节码执行。 1 + 2可以用反向波兰语表示为1 2 +。 为什么? 好吧,当我们将其放在堆栈上时,一切都变得清晰了……
好的,在字节码中,我们实际上会看到操作码(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
现在真的很烦你,对吧? :)如果将这个方法体可视化为数组,这就是我们得到的:
请注意,每条指令都有一个十六进制表示,因此使用它我们实际上会看到以下内容:
如果在十六进制编辑器中打开它,我们实际上可以在类文件中看到它:
实际上,我们可以在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循环时,执行流程将跳很多次。 假定numbers变量是同一类中的静态字段,该字节码如下所示:
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
可以在字节码中使用一堆算术操作码和类型命令组合,包括以下内容:
以及许多类型转换操作码,这些类型转换操作码在将整数分配给long类型的变量时很重要。
在我们的珍贵示例中,我们将一个整数传递给采用双精度值的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 )
翻译自: https://www.javacodegeeks.com/2013/12/mastering-java-bytecode.html
字节跳动
字节跳动_掌握Java字节码相关推荐
- java字节码_掌握Java字节码
java字节码 嘿! Happy Advent:D我是ZeroTurnaround的技术布道者Simon Maple( @sjmaple) . 您知道, JRebel伙计们! 由于编写了类似JRebe ...
- java 字节码对象_通过java字节码分析学习对象初始化顺序
mockery.checking(new Expectations() { { one(new Object()).toString(); will(returnValue("") ...
- 字节跳动、美团java后端社招面试题:多线程+分布式+算法+数据库+JVM+微服务
写在前面: 字节跳动是近几年发展特别迅速的互联网公司,凭借短视频平台[抖音]和自由创作平台[今日头条]等一系列以内容创造价值的应用异军突起,已经成为动摇BAT地位的一大狠角色. 同时,字节跳动公司也是 ...
- 太out了,字节跳动、美团java后端社招题:多线程+分布式+算法+数据库+JVM,你都不知道?
写在前面: 字节跳动是近几年发展特别迅速的互联网公司,凭借短视频平台[抖音]和自由创作平台[今日头条]等一系列以内容创造价值的应用异军突起,已经成为动摇BAT地位的一大狠角色. 同时,字节跳动公司也是 ...
- fileoutputstream 字节乱码_吃透Java IO:字节流、字符流、缓冲流
前言 有人曾问fastjson的作者(阿里技术专家高铁):"你开发fastjson,没得到什么好处,反而挨了骂背了锅,这种事情你为什么要做呢?" 高铁答道:"因为热爱本身 ...
- 【每日面试】2021字节跳动番茄小说Java二面
作者:Yyyilia 链接:https://www.nowcoder.com/discuss/766918?source_id=discuss_experience_nctrack&chann ...
- java工程师去字节飞书可以,字节跳动飞书Java后端开发暑假实习一面(过了)
字节跳动----飞书一面(过了) 2021.3.9 30分钟 0.自我介绍 1.项目提问(7分钟) 2.TCP的四次挥手(八股文) 3.网络7层模型(八股文) 4.HTTP和HTTPS(八股文) 5. ...
- 【校招】面试_字节跳动_客户端开发工程师_二面
1 面试信息 面试形式:视频面试 面试时间:2020-03-10 14:00:00GMT+08:00 面试时长:约半小时 面试职位:客户端开发工程师-产品研发和工程架构部 2 面试问题 1.A.B轮流 ...
- 【字节跳动内推】字节跳动互娱研发-成都抖音的HC真的多!-帮看状态改简历
来字节-成都抖音 一起Dance吧!少年!ps:客户端开发HC很多的!!!ios底层是C语言,android底层是java,现在用kotllin,师弟师妹们只要学C.C++.java都可以try一tr ...
最新文章
- 贝塞尔曲线开发的艺术
- VTK:图片之ImageGradientMagnitude
- Windows Server 2012配置开机启动项
- 《概率统计简明教程》
- JavaScript 检测当前浏览器内核并提示下载谷歌Chrome浏览器
- 学习蓝桥杯之单片机1——软件环境搭建
- MacBook 右键查询英文查词无法翻译成中文
- python实现金字塔图案
- 小白学习PYTHON之路---PY文件转换成EXE可执行文件
- 微擎we7模块和模板安装方法
- HTML 文档可以映射为,将PDF文档转换为可通过URL访问的HTML文档的最佳方法
- INS 、AHRS、VRU、IMU的区别与联系
- UVM-config机制
- Hadoop机架感知配置及配置问题解决
- EasyRecovery热门免费数据检测修复软件
- 1-第一篇入住博客自我介绍
- 程序员的春天来了,最美赏花旅游地十大攻略
- 【雅思】【绿宝书错词本】List1~12
- 5W1H分析法 什么是5W1H分析法?
- 潇 洒 女 兵 分 解
热门文章
- 动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
- 莫斯科保卫战之PHP-502 Bad Gateway
- 哈希表建立及冲突处理
- 我的node+express小例子
- linux-Tcp IP协议栈源码阅读笔记
- lb集群lvs的3种模式
- 用putty生成密钥SSH远程登录(解决)
- python3 报错 ‘builtin_function_or_method‘ object has no attribute 解决方法
- linux 安装 nodejs
- RSA 密钥 明文 密文 长度介绍