从字节码指令看重写在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 ...
随机推荐
- ASP.NET—011:JavaScript报错常见问题
相信大家都写过JavaScript.JS由于语法以及自己须要实现的业务的原因,可能在一个页面上要写长篇大论.或者单独写js文件写了好几百K.JS不可否认给Web编程带了很多的方便. 可是假设JS发生了 ...
- Spring Batch(4): Job详解
Spring Batch(4): Job详解 2016-03-26 18:46 870人阅读 评论(1) 收藏 举报 分类: Spring(6) 版权声明:本文为博主原创文章,未经博主允许不得转载 ...
- 为代码减负之<三>视图(SQL)
在设计数据库时为了降低数据冗余.一般都会依照三范式去设计,但有时我们在查询时须要通过一字段获取跟这 个字段相关联的好几个字段.可是他们又分布在不同的表中,这时候假设依照正常途径走的话须要同一时候查询好 ...
- BZOJ3170: [Tjoi2013]松鼠聚会
[传送门:BZOJ3170] 简要题意: 给出n个点的坐标,规定两个点的距离=max(|x1-x2|,|y1-y2|) 要求选出一个点,使得这个点到所有点的距离和最小 题解: 切比雪夫转换例题 将一个 ...
- xargs用例一个
ls -a *.doc|awk -F. '{print $1}' |xargs -I {} java -jar ~/soft/jodconverter-2.2.2/lib/jodconverter-c ...
- [SCOI 2009] 生日快乐
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1024 [算法] 直接DFS,即可 [代码] #include<bits/std ...
- BZOJ 3624 并查集 (Kruskal)
思路: 先把所有能加上的水泥路都加上 判断哪些是必加的鹅卵石路 再重新做一遍最小生成树 加上必加的鹅卵石路 一直加鹅卵石路 判一下是不是=k 最后加上水泥路就好了 //By SiriusRen #in ...
- Hua Wei 机试题目一
一.身份证号码验证 题目描述: 我国公民的身份证号码特点如下:1. 长度为18位:2. 第1-17位只能为数字:3. 第18位可以是数字或者小写英文字母x.4. 身份证号码的第7~14位表示持有人生日 ...
- struts2学习之基础笔记4
拦截器 1.自定义拦截器类,必须继承AbstractInterceptor类(抽象类) 重写public String intercept (ActionInvocation arg0) 2.在Str ...
- CSS3背景 制作导航菜单综合练习题
CSS3背景 制作导航菜单综合练习题 小伙伴们,根据所学知识,使用CSS3实现下图的导航菜单效果 任务 1.制作导航圆角 提示:使用border-radius实现圆角 2.制作导航立体风格 提示:使用 ...