从字节码指令看重写在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 ...
随机推荐
- 《UML精粹》第三章 -类图的基本概念
第三章 类图:基本概念 类图可用来描写叙述系统中各种对象的类型.也可描绘出对象间各种各样的静态关系.此外.类图中也能够秀出类的性质(property)与操作(operation),以及可应用到对象间连 ...
- 串口通讯 ADC0804 数码管
#include<reg52.h> #include<stdio.h> #include<intrins.h> #define uchar unsigned cha ...
- TeamTalk Android代码分析(业务流程篇)
TeamTalk Android代码分析(业务流程篇) 1.1 总体结构 1.总体结构有点类似MVC的感觉,模块结构从上向下大体是: UI层:Activity和Fragment构成,期间包括常用的一些 ...
- $scope angular在controller之外调用
1.定义 var m = angular.module('ddd',[]); m.controller('ctrl',['$scope',function ($scope) { }]); 2.外部调用 ...
- php面向对象之构造函数和析构函数
php面向对象之构造函数和析构函数 简介 php面向对象支持两种形式的构造函数和析构函数,一种是和类同名的构造函数(php5.5之前),一类是魔术方法(php5.5之后).与类名相同的构造函数优先级比 ...
- 基本类型转换成NSNumber类型
int i=100; float f=2.34; NSNumber *n1=[NSNumber numberWithInt:i]; NSNumber *n2=[NSNumber numberWithF ...
- ubuntu下谷歌浏览器字体模糊解决方案
一般来说,这种问题应该是缺少字体造成的,因此重新安装字体就可以解决问题. 1.双系统下,查找Windows C盘下:Windows/font/???? ???可在3中查找相应字体 2.分别双击安装那两 ...
- Scrapy Architecture overview--官方文档
原文地址:https://doc.scrapy.org/en/latest/topics/architecture.html This document describes the architect ...
- Java io 操作
package tlistpackage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFou ...
- Android 高大上的侧滑菜单DrawerLayout,解决了不能全屏滑动的问题
DrawerLayout预览 DrawerLayout主要功能就是 实现侧滑菜单效果的功能,并且可以通过增加一些设置来实现高大上的效果,那么就请看动态图: 注意左上角那个图标,有木有很好玩,哈哈. ...