从字节码指令看重写在JVM中的实现
Java是解释执行的。包含动态链接的特性。都给解析或执行期间提供了非常多灵活扩展的空间。面向对象语言的继承、封装和多态的特性,在JVM中是怎样进行编译、解析,以及通过字节码指令怎样确定方法调用的版本号是本文例如以下要探讨的主要内容。全文环绕一个多态的简单举例来看在JVM中是怎样实现的。
先简介几个概念。对于字节码运行模型及字节码指令集的相关概念能够參考之前的一篇介绍http://blog.csdn.net/lijingyao8206/article/details/46562933。
一、方法调用的解析
在Class文件里。方法调用是常量池中的一个符号引用,在载入的解析期或者执行时才干确定直接引用。
以下两种指令是在解析期就能够确定直接引用,调用的相应方法也叫作非虚方法。
1、 invokestatic 主要用于调用静态方法
2、 invokespecial 主要用于调用私有方法,构造器,父类方法。
以下两种是在执行时才干确定直接引用的。可是除了final方法,final方法也能够在解析期确定方法的调用版本号。
1、 invokevirtual 虚方法,不确定调用那一个实现类
2、 invokeinterface 接口方法,执行时才干确定实现接口的对象。
二、动态分派
先回想一下静态和动态分派的概念。
Java中,全部以静态类型来定位方法执行版本号的分派动作,都称为静态分派。事实上也就是重载(Overload)就是一种典型的静态分派,在编译期就能够知道方法调用的实际版本号。相对得。动态分派是须要在执行期才干确定方法的版本号,也就是直接引用。一种典型应用就是重写(OverWrite)。在调用invokevirtual指令时,把常量池中的类方法符号引用解析到直接引用的过程就是重写的过程,执行期依据实际类型确定方法的执行版本号。
三、解释运行
Java的解释运行机制,使jaavc编译的过程涵盖了从程序代码的语法、词法分析,再到AST(抽象语法树)生成线性的字节码指令流的过程。
而解释运行是在JVM内部。基于栈的指令集提供了整个平台的可移植性支撑,所以这也是java运行慢的要因,由于不像编译运行。过程中须要很多其它的出入栈指令,而栈又是内存的一个区块。对内存的频繁訪问减少了性能。
四、重写(OverWrite)举例
以下通过一个简单的样例来看一下重写在JVM中的字节码运行模型。
父类:
package bytecode; /**
* Created by yunshen.ljy on 2015/7/27.
*/
public class Wine { public String drink(int ml) {
return "drink " + ml + "ml wine";
} }
子类:
package bytecode;
/**
* Created by yunshen.ljy on 2015/7/27.
*/
public class Beer extends Wine{ /**
* 重写父类方法,实现多态
*/
public String drink(int ml){
return "drink " + ml +"ml beer";
}
}
调用:
package bytecode; /**
* Created by yunshen.ljy on 2015/7/26.
*/
public class MethodInvotionTest { public String drink(int ml) { Wine wines = new Beer();
return wines.drink(ml); }
}
我们都知道。方法调用返回的结果是“drink XX ml beer”。
可是在编译期,字节码中的指令是无法确定实际调用的方法版本号的。
以下看一下调用方法的字节码结构。
public java.lang.String drink(int);
descriptor: (I)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=2
0: new #2 // class bytecode/Beer
3: dup
4: invokespecial #3 // Method bytecode/Beer."<init>":()V
7: astore_2
8: aload_2
9: iload_1
10: invokevirtual #4 // Method bytecode/Wine.drink:(I)Ljava/lang/String;
13: areturn
LineNumberTable:
line 10: 0
line 12: 8
第一条指令new 创建对象。把引用入栈指令。new 指令后面的#4就是前文提到的。对于执行时常量池的一个引用,仅仅是javap命令处理成比較易懂的方式来显示。
接着。后面的dup 指令复制刚放入的引用(操作数栈栈顶的值的复制而且将这个“副本”放到栈顶)。Invokespecial 指令就是之前介绍的非虚方法调用指令,在操作数栈中通过当中的一个引用调用Beer的构造器,初始化对象,让还有一个同样引用指向初始化的对象,然后前一个引用(this)弹出栈。
astore_2把引用保存到局部变量表中的索引2位置中。aload_2
把刚才局部变量表中索引2处的值压入操作栈。
iload_1将int參数,也就是局部变量表中索引1处的值压入操作数栈。
Invokevirtual指令将运行操作数栈中的两个值出栈,运行方法调用。指令后的#4仅仅是常量池中的一个符号引用,仅仅有在运行时。才干确定方法的直接引用。
最后的areturn语句将方法运行结果的值出栈,消除当前的栈帧,假设上层有对于当前方法的继续调用,那么会将调用的方法的栈帧设置成当前栈帧(current stack frame)。
以下通过对照每条指令运行后,局部变量表和操作数栈中的值来加强一下上述的出栈、入栈操作。
首先通过stack=2, locals=3, args_size=2 ,先知道了方法的局部变量表是占用了3个slot,操作数栈的size是2个slot。
而由于我们的方法是实例方法(非类方法),所以隐含的thiskeyword的指令我们先描写叙述到局部变量表的第一个位置。占用一个slot。(以下不再画出常量池的结构,可參照calss文件自行分析)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
这里仅仅是个简单的举例。和一些知识的回想。知道JVM的运行引擎和字节码指令的一些概念,会让我们对于程序运行的结果预期更加准确,也更易理解一些java语言版本号的设计模式实现。这里仅仅是抛砖引玉,对于其它几种方法调用的指令。也建议大家尝试进行很多其它的分析。
从字节码指令看重写在JVM中的实现的更多相关文章
- 从jvm字节码指令看i=i++和i=++i的区别
1. 场景的产生 先来看下下面代码展示的两个场景 @Testvoid testIPP() { int i = 0; for (int j = 0; j < 10; j++) { i = i++; ...
- 【JVM源码解析】模板解释器解释执行Java字节码指令(上)
本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...
- [四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式
前言简介 前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明 想要了解JVM的运行机制,以及如何优化你的代码,你还需要了解一下,java编译器到底是 ...
- jvm理论-字节码指令
Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成. 基本数据类型 1.除了l ...
- JVM学习第三天(JVM的执行子系统)之字节码指令
早上看了Class类文件结构,晚上继续来看字节码指令,毕竟谁也不是一步登天的(说白了还是穷); 字节码指令 Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode ...
- JVM总括三-字节码、字节码指令、JIT编译执行
JVM总括三-字节码.字节码指令.JIT编译执行 目录:JVM总括:目录 java文件编译后的class文件,java跨平台的中间层,JVM通过对字节码的解释执行(执行模式,还有JIT编译执行,下面讲 ...
- 深入了解java虚拟机(JVM) 第十章 字节码指令
一.字节码指令的含义 Java字节码指令由一个字节长度的,代表某种特定操作含义的数字(操作码)以及其后的零至多个代表此操作所需参数(操作数).此外字节码指令是面向操作数栈的,这里操作数栈在功能上对应实 ...
- JVM学习笔记——字节码指令
JVM学习笔记——字节码指令 字节码 0与 1是计算机仅能识别的信号,经过0和1的不同组合产生了数字之上的操作.另外,通过不同的组合亦产生了各种字符.同样,可以通过不同的组合产生不同的机器指令.在不同 ...
- JVM 字节码指令手册 - 查看 Java 字节码
JVM 字节码指令手册 - 查看 Java 字节码 jdk 进行的编译生成的 .class 是 16 进制数据文件,不利于学习分析.通过下命令 javap -c Demo.class > Dem ...
随机推荐
- rails的数据库查询方法
rails的数据库查询方法 学习了:http://blog.csdn.net/menxu_work/article/details/8664962 学习了:http://www.cnblogs.com ...
- [Spring实战系列](17)编写切点与声明切面
切点用于准确定位应该在什么地方应用切面的通知. 切点和通知是切面的最基本元素. 在Spring AOP中,须要使用AspectJ的切点表达式语言来定义切点. 关于Spring AOP的AspectJ切 ...
- PDF转EPUB格式电子书经验总结
依据本人将PDF转换为EPUB电子书的经验,总结整理了这篇文章.因本人水平有限,难免有错误和不足之处,望大家及时批评指正. 写这篇文章时,假定读者已经会使用文中所列出软件的基本操作,比方如何用No ...
- Android Camera解析(上) 调用系统相机拍摄照片
开发中我们常须要通过相机获取照片(拍照上传等).一般通过调用系统提供的相机应用就可以满足需求:有一些复杂需求还须要我们自己定义相机相关属性,下篇我们会涉及到. 首先我们来研究怎样简单调用系统相机应用来 ...
- TS3
let [first, ...rest] = [1, 2, 3, 4]; console.log(first); // outputs 1 console.log(rest); // outputs ...
- mysql事务的开启
mysql事务的开启 对于一个MYSQL数据库(InnoDB),事务的开启与提交模式无非下面这两种情况: 1>若参数autocommit=0,事务则在用户本次对数据进行操作时自动开启,在用户执行 ...
- WCF:目录
ylbtech-WCF:目录 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 6.返回顶部 作者:ylbtech出处:http://ylbtech.c ...
- Python multiprocessing.Manager介绍和实例(进程间共享数据)
Python中进程间共享数据,处理基本的queue,pipe和value+array外,还提供了更高层次的封装.使用multiprocessing.Manager可以简单地使用这些高级接口. Mana ...
- Caffe Batch Normalization推导
Caffe BatchNormalization 推导 总所周知,BatchNormalization通过对数据分布进行归一化处理,从而使得网络的训练能够快速并简单,在一定程度上还能防止网络的过拟合, ...
- Day 02 - 01 计算机的操作系统
操作系统 场景:有以下三个人 1.庄园主 -->相当于人类 2.管家 --> 把人类要传达的信息分配给奴隶,相当于操作系统 3.奴隶 -->为人类干活的机器 1.操作系统做了什么? ...