java字节码理解——Java bytecode:翻译和解读

本篇博客是对Java bytecode:这篇文章的翻译和解读,原文链接在这

http://www.ibm.com/developerworks/library/it-haggar_bytecode/index.html

如有不正之处还请各位指教,不喜勿喷,相互交流才能进步。

下面正片开始

生成java字节码:

javac Employee.java

javap -c Employee > Employee.bc

Generating bytecode

先将java源码进行编译后再用javap命令进行反编译并添加-c参数来获得类的字节码。获得字节码如下:

根据原文解读可大致猜测,Employee.java源码应该是这样的:

由此可以发现:前五行的字节码是用哪个类生成的,这个类的定义,这个类是从哪个类继承的,这个类的构造方法和其他的方法。下一步字节码将这个类的构造方法罗列出来,之后又将这个类的所有方法和与之相关的字节码用字典序罗列出来(这里我尝试了一下发现字节码中方法并没有按照字典序排序..不知是否是我的理解错误,测试图如下)。

抛开上面无关紧要的部分,继续。。。

这时候你可能会发现,特定的操作码的前缀a和i

这个操作码的前缀’a’表示的意思为:说明正在操作的是一个对象的引用。

同理’i’表示的意思为:说明正在操作的是一个整型变量

除了’a’和’i’还有以下几种操作码前缀:

‘b’:说明正在操作的是byte类型的变量

‘c’:说明正在操作的是char类型的变量

‘d’:说明正在操作的是double类型的变量

等等。。。

这里要注意:独立的代码一般会被jvm解析为操作码,多重的操作码指令一般会被解析为字节码。

The details

这里为了更深入的理解jvm是如何执行字节码的,我们要理解一下jvm。

Jvm是基于堆栈的,对于jvm来说每一个线程都会有一块独立的堆栈,堆栈中存储着栈帧,当线程中有方法被调用的时候就会创建一块栈帧并将其压入栈中,栈帧又由如下几块组成:操作数栈,局部变量区和一块当前线程所拥有的的类的引用(类的引用保存在常量池中)。

对于局部变量区来说,它既存储方法的参数,也存储方法中产生的一些中间变量。对于普通方法(静态方法)而言局部变量区首先存储的是方法的参数(从0开始),接着再存储方法中产生的局部变量。但是对于构造方法或者是实例方法而言,首先存储的是对象的引用(从0开始),接着第一个参数存1号位置,第二个参数存第二个位置,以此类推。对于静态方法来说,由于静态方法没有是类所拥有的,与对象无关,因此它的第一个参数存0号位置,第二个参数存1号位置,以此类推。。。

局部变量区的大小在编译的时候已经决定了,它主要取决于参数和中间变量的个数和大小

操作数栈用栈来push和pop值,一些特定的指令集会将操作数压入栈中,其他的指令会将这些操作数取走,并将执行的结果再次压入栈中。

上面一段是java源码,下面一段是相应的字节码。

这一段字节码由3个指令集组成。

先看第一个指令集 aload_0 首先根据之前讲的,’a’开头说明操作的是一个引用,结尾的0表示的是从局部变量区中取出放在0号位置的那个变量放入操作数栈,那么为什么要用aload而不用iload呢?我们看一下这个方法是一个实例方法,实例方法的局部变量区的0号位置存储的是对象引用(this),因此要用’a’开头。所有的一切是那么解释通了。还有一个问题,jvm为什么要取this引用呢?因为我们要用this引用来传递实例数据,名字等信息。

再看第二个指令集getfield #5,这个指令用来获取变量从一个对象中,当这个指令执行的时候,处于操作数栈顶端的this引用被拿出,和后面的#5组成一个索引去常量池中寻找相应属性(这里是找name,因为要获取name属性的引用)的引用,当这个引用找到并抓取了之后,将结果放入操作数栈中。

最后一个指令集areturn ,从这个方法中返回对应返回值的引用,并将这个返回值的引用从操作数栈中取出,压入调用方法的操作数栈中(栈帧中)。

下面讲一下最左边的数字是怎么来的,先上两张图:

相信你一定能够看懂(我不会说是因为我懒而不想翻译那么大一段英文。。。这里其实体现了java的平台无关性)

接着我们看一下构造函数的字节码:

首先第一行字节码跟我们上面分析一样,将构造方法的this引用取出来压入操作数栈中。

第二行字节码是调用父类的构造方法,因为所有的没有显示继承的类都隐式的继承的Object类,因此这里是调用了Object类的构造方法。也就是说,该构造方法的java源码其实应该是这样的:

跟之前分析的一样,当这行字节码执行完后,this引用从操作数栈中移除。

接下去的两行字节码aload_0和aload_1,就是将this引用和构造方法的第一个参数取出(不明白为什么的小伙伴请再阅读一遍局部变量区的那里哈),放入操作数栈中。

接下去的putfield #5这行字节码就是将上一步压入操作数栈中的this引用和strName值取出,并通过this和#5找到相应的strName引用将这个strName值赋给它。

下面那个给idNumber赋值的同理。

最后来看看最后5步aload_0 aload_1 iload_2 invokespecial #6 <Methodvoid storeData(java.lang.String, int)> return

前三条指令分别将this引用,strName值,idNumber值分别取出并压入操作数栈,注意this引用必须要被压入,因为这个实例方法正在被调用。如果这个方法是静态方法的话,this引用就不必被压入栈中,但是strName和idNumber必须要被压入到操作数栈中,因为他们是storedData方法的参数。因此当storedData方法执行的时候,this引用,strName和idNumebr分别占据storedData方法的局部变量区的0,1,2个索引。

Size and speed issues

这个模块就是比较两种相同功能的代码用怎么写比较快,解析成的字节码比较数量比较少。

下面看一下这两种线程同步方式解析所产生的的字节码数量的区别:

原文说道第一种方式大概要比第二种方式要快13%左右。但是我认为这仅限于方法内部都需要同步的情况,当方法只需要部分同步的时候,这时候在高并发情况下,直接加锁的方式效率显然要低于synchrnized块的方式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值