深入理解jvm之分派
来源:互联网 发布:程序员怎样创业 编辑:程序博客网 时间:2024/06/07 20:14
之前费了好大劲,一直想搞清楚 .class文件是个什么东东,因为我知道 .java 文件编译后是字节码的二进制文件,所以。。。有点蒙,因为在我的理解中,二进制文件就是文件中只有0和1的文件。。。。其实class文件用十六进制编辑器打开后就是16进制的字符了,那么,其实这是将4个数字合并的结果。.class文件就是字节码文件,用javap指令可以查看到对应的解析。
字节码文件是怎么执行的呢?这个和虚拟机的不同有关,有些是先解释,形成另一种虚拟机的指令,再执行,有些是JIT编译器编译成虚拟机所在主机cpu的本地指令集执行(可是cpu指令集是个什么东东),这样就和物理机打交道了。
粘贴一段文字:
汇编是离机器码最近的一个人类可阅读可编写的语言形式。
机器(CPU)为了运算速度,被设计成只能阅读010101这样的文字的东西。
而人类不可能用0 和 1 ,来写程序,阅读和理解和修改起来太费劲了,效率太低。
所以最开始就有了汇编语言,人类用汇编语言来写人类看得懂的程序,例如:MOV AH,15
之后“编译器”这个程序会把人类编写的程序,翻译成CPU能理解的010101.........
其实编译执行,就是产生本地代码(Native Code)的过程,其实就是生成宿主机处理器的指令集的过程,本地代码并不是0101...,它是指令集。。机器(CPU)为了运算速度,被设计成只能阅读010101这样的文字的东西。
而人类不可能用0 和 1 ,来写程序,阅读和理解和修改起来太费劲了,效率太低。
所以最开始就有了汇编语言,人类用汇编语言来写人类看得懂的程序,例如:MOV AH,15
之后“编译器”这个程序会把人类编写的程序,翻译成CPU能理解的010101.........
Class文件包括java虚拟机指令集和符号表已经若干其他辅助信息(摘自深入理解JVM虚拟机),从这里可以看出javap命查看的字节码指令确实就是Class文件。
扯远了。。。以上只是前提,下面进入讨论的主题。
JVM分派:
方法调用是如何确定调用版本(调用哪一个方法)的呢?
分为静态和动态,静态就是在编译后解析过程就能确定调用版本,动态就是在运行解析时才能确定哪一个版本。
这里其实也是对java的多态有了更深一步的认识,编译期间只知道父类型(或接口类型),直到运行期间才能确定实际类型。
先来看一个例子:
public class Dispatch1{ static abstract class Human { } static class Man extends Human { } static class Woman extends Human{} public void sayHello(Human guy) { System.out.println("hello,guy!"); } public void sayHello(Man guy) { System.out.println("hello,gentleman!"); } public void sayHello(Woman guy) { System.out.println("hello,lady!"); } public static void main(String[] args){Human man=new Man();Human woman=new Woman();Dispatch1 dp=new Dispatch1();dp.sayHello(man);dp.sayHello(woman);}}
输出:
hello,guy!
hello,guy!
这个其实是重载,Human其实是静态类型,编译期间就可以直到,Man Woman 是实际类型,运行期间才可以确定。重载的时候,是根据参数的静态类型确定的,而不是实际类型。
hello,guy!
这个其实是重载,Human其实是静态类型,编译期间就可以直到,Man Woman 是实际类型,运行期间才可以确定。重载的时候,是根据参数的静态类型确定的,而不是实际类型。
接下来我们继续来看:
public class SuperTest {public static void main(String[] args) {new Sub().exampleMethod();}}class Super {void interestingMethod() {System.out.println("Super's interestingMethod");}void exampleMethod() {interestingMethod();}}class Sub extends Super {void interestingMethod() {System.out.println("Sub's interestingMethod");}}输出:
Sub's interestingMethod
public class SuperTest {public static void main(String[] args) {new Sub().exampleMethod();}}class Super {private void interestingMethod() {System.out.println("Super's interestingMethod");}void exampleMethod() {interestingMethod();}}class Sub extends Super {void interestingMethod() {System.out.println("Sub's interestingMethod");}}输出:
Super‘s interestingMethod
代码二的Super类javap输出
Compiled from "SuperTest.java"
class Super {
Super();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
void exampleMethod();
Code:
0: aload_0
1: ldc #5 // String aa
3: invokespecial #6 // Method interestingMethod:(Ljava/l
ang/String;)I
6: pop
7: return
}
class Super {
Super();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
void exampleMethod();
Code:
0: aload_0
1: ldc #5 // String aa
3: invokespecial #6 // Method interestingMethod:(Ljava/l
ang/String;)I
6: pop
7: return
}
代码一的Super类javap输出 写道
Compiled from "SuperTest.java"
class Super {
Super();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
int interestingMethod(java.lang.String);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/
io/PrintStream;
3: ldc #3 // String Super's interestingMethod
5: invokevirtual #4 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
8: iconst_1
9: ireturn
void exampleMethod();
Code:
0: aload_0
1: ldc #5 // String aa
3: invokevirtual #6 // Method interestingMethod:(Ljava/l
ang/String;)I
6: pop
7: return
}
class Super {
Super();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
int interestingMethod(java.lang.String);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/
io/PrintStream;
3: ldc #3 // String Super's interestingMethod
5: invokevirtual #4 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
8: iconst_1
9: ireturn
void exampleMethod();
Code:
0: aload_0
1: ldc #5 // String aa
3: invokevirtual #6 // Method interestingMethod:(Ljava/l
ang/String;)I
6: pop
7: return
}
两边的不同我通过加粗来说明了.,在Super类的exampleMethod方法中调用interestingMethod方法的指令是不同的,代码二采用的是invokespecial 而代码一采用的是invokevirtual .
写道
· invokespecial - super方法调用、private方法调用与构造器调用
· invokevirtual - 用于调用一般实例方法(包括声明为final但不为private的实例方法)
· invokevirtual - 用于调用一般实例方法(包括声明为final但不为private的实例方法)
其中
写道
invokespecial调用的目标必然是可以静态绑定的,因为它们都无法参与子类型多态;invokevirtual的则一般需要做运行时绑定
invokespecial调用的目标必然是可以静态绑定的,因为它们都无法参与子类型多态;invokevirtual的则一般需要做运行时绑定所以说,有private 修饰的方法,是可以在编译器就找到调用版本的。invokerspecial调用的目标为interestingMethod,所以在编译器就能确定,一定是静态绑定。
其他的例子,
public class Dispatch{ static class QQ{}; static class Q1 extends QQ{}; static class Q2 extends QQ{}; public static class Father { public void hardChoice(QQ arg) { System.out.println("father choose qq"); } public void hardChoice(Q1 arg) { System.out.println("father choose q1"); } public void hardChoice(Q2 arg) { System.out.println("father choose q2"); } } public static class Son extends Father { public void hardChoice(QQ arg) { System.out.println("son choose qq"); } public void hardChoice(Q1 arg) { System.out.println("son choose q1"); } public void hardChoice(Q2 arg) { System.out.println("son choose q2"); } } public static void main(String[] args){ /* * 两个宗量:方法的接收者和参数,我的理解是,参数是静态的,接收者是动态的,即直等到运行时才能确定接收者的实际类型 */Father father=new Father();QQ q =new Q1();QQ q1=new Q2();father.hardChoice(q);//相当于father的重载,静态绑定,输出father choose qqFather son=new Son();son.hardChoice(q);//动态分配是单分派,参数已经在静态期间确定了,实际类型运行时确定,那么为什么不在运行时确定q的实际类型呢?所以,我认为,
//动态单分派,指的是接收者的单分派。}}
补充---------------------------------------》
关于静态分派和动态分派的理解,深入理解JVM书中所说:只要能被invokerstatic和invokerspecial指令调用的方法,都可以在解析阶段确定唯一的调用版本,就是说,只要在解析阶段能确定到底谁是要执行的目标函数时,就进行静态绑定了,那么我们在看上面的例子:
new sub()在编译期还不知道实际类型,只知道为Super类型,所以去找Super的exampleMethod方法
父类的exampleMethod方法 有 invokerspecial 指令,去找 interestingMethod,找到了,进行静态绑定
如果private变为public ,从Super类找的时候,exampleMethod 并不能找到调用目标,所以不能绑定,只有在运行时进行动态绑定。
0 0
- 深入理解jvm之分派
- 深入理解JVM之七:静态分派与动态分派
- 深入理解JVM之七:静态分派与动态分派
- 深入理解JVM之七:静态分派与动态分派
- 深入理解JVM九---分派
- 【深入理解JVM】:解析与分派
- JVM之方法调用-分派
- jvm 堆栈之深入理解
- 深入理解JVM之JVM内存参数
- 对JVM分派概念的理解
- 深入理解java解析、分派和绑定
- JVM 方法调用之动态分派
- JVM 方法调用之动态分派
- 20150906 深入理解JVM之入门笔记
- 深入理解JVM之垃圾回收详解
- 深入理解JVM之垃圾回收详解
- 深入理解JVM之垃圾回收详解
- 深入理解JVM之垃圾回收详解
- ant常用功能
- linux进程通信之消息队列
- java精度运算——BigDecimal
- JSP动态生成,点击添加表单table行-01(点击添加的时候会提交数据给ACTION)
- disappointed
- 深入理解jvm之分派
- android 布局relativeLayout实现底部标签
- warshall's algorithm
- C#OOP之七 多态的学习和使用
- 第九周上机实践项目——项目3-人数不定的工资类
- php之cookie【1】
- HDU 5242 Game
- 关于安卓开发环境配置方面的一些经验教训—Android Studio
- 【MYSQL】一台centos6.5主机建两个mysql实例实现主从复制环境