深入理解Jvm--Java静态分配和动态分配完全解析
jvm中分配Dispatch的概念
分派是针对方法而言的,指的是方法确定的过程,通常发生在方法调用的过程中。分派根据方法选择的发生时机可以分为静态分派和动态分派,其中对于动态分派,根据宗量种数又可以分为单分派和多分派。实际上指的是方法的接收者和属性的所有者的类型确定(determine by atual type or determine by static type)。根据类型确定发生在运行期还是编译期以及依据实际类型还是静态类型,可以将Dispatch分为动态分配Dynamic Dispatch和静态分配Static Dispatch两类。
虚方法和非虚方法
在理解动态绑定和静态绑定之前必须先理解虚方法和非虚方法。
①非虚方法
只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本,符合这个条件的有静态方法、私有方法、实例构造器、 final方法,它们在类加载的时候就会把符号引用解析为该方法的直接引用。这些方法可以称为非虚方法。
②虚方法
非私有的实例方法等。
静态分配Static Dispatch
静态分派的典型应用是方法重载overlord。静态分派指的是在编译期间进行的方法选择,通常以方法名称,方法接收者和方法参数的静态类型来作为方法选择的依据。这些可以静态分派的方法一般都具有“签名唯一性”的特点(签名只考虑参数的静态类型而不管参数的实际类型),即不会出现相同签名的方法,因此可以在编译期就实现方法确定。Java中的非虚方法(主要包括静态方法,私有方法,final方法等,这些方法一般不可重写,故而不会有相同签名的情况出现)通常仅需要静态分派就可以实现方法的最终确定,更特别一点的例子是静态方法的隐藏,也是利用了静态分派,后面会专门讲解。虚方法的重载在编译时也用到了静态分派(尽管虚方法的调用在运行时还会涉及动态分派)。静态分配实例:
public class StaticDispatch{
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();
StaticDispatch sr=new StaticDispatch();
sr.sayHello(man);
sr.sayHello(woman);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
运行结果:
hello,guy!
hello,guy!
- 1
- 2
动态分配Dynamic Dispatch
动态分派的典型应用是方法重写override动态分派是指方法的确定在run-time才能最终完成。使用动态分派来实现方法确定的方法一般在编译期间都是一些“不明确”的方法(比如一些重写方法,拥有相同的方法签名并且方法接收者的静态类型可能也相同),因此只能在运行时期根据方法接收者和方法参数的实际类型最终实现方法确定。Java中的虚方法(主要指实例方法) 通常需要在运行期采用动态分派来实现方法确定(利用invokevirtual指令获取方法接收者的实际类型)。动态分配实例:
public class DynamicDispatch{
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{
@Override
protected void sayHello(){
System.out.println("man say hello");
}
}
static class Woman extends Human{
@Override
protected void sayHello(){
System.out.println("woman say hello");
}
}
public static void main(String[]args){
Human man=new Man();
Human woman=new Woman();
man.sayHello();
woman.sayHello();
man=new Woman();
man.sayHello();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
运行结果:
man say hello
woman say hello
woman say hello
- 1
- 2
- 3
相关笔试面试题
class Person{
int age = 30;
int getAge(){
return age;
}
}
class Man extends Person{
int age = 40;
int height = 160;
int getAge(){
return age;
}
}
public class Demo{
public static void main(String[] args){
Person a = new Man();
// a.age内部主要通过如下字节码实现:
// getfield #5 // Field test/Person.age:I
System.out.println(a.age);
// a.getAge()内部主要通过如下字节码实现:
// invokevirtual #7 // Method test/Person.getAge:()I
System.out.println(a.getAge());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
运行结果:
30
40
- 1
- 2
上面题目不仅涉及到Dispatch而且涉及到了Binding,
Static Binding
类型在编译期就已经可以确定,并且该类型确定在运行期保持不变,即最终通过静态类型确定该变量类型。Java中,在Java中,静态绑定通常用于属性所有者的类型绑定,非虚方法(类方法,私有方法,构造器方法,final方法)接收者的类型绑定,以及方法参数的类型绑定。
上例中,age属性是对象属性,age属性的所有者(对象a)在此次访问中是静态绑定,因此这里对象a的类型在编译期被确定为a的静态类型Person,并且该类型确定后在运行期执行getfield指令时也不会发生改变,最后”a.age”调用的是a的静态类型Person的age属性值。这里也涉及到了属性隐藏的问题:父类和子类有同名域时,域的访问是通过域的所有者的静态类型决定的。比如上面例子中如果想访问子类Man中的age,则必须将对象a强制转型为Man,或者在当时创建之初就声明为Man类型而非Person类型。
通过静态绑定来实现访问对象属性所有者类型绑定的好处在于:编译期就可以确定最终类型,避免了动态查找,高效快速,但是是以牺牲一部分灵活性为代价的。
Dynamic Binding
类型在运行时才能最终确定,通过最终实际类型(运行时类型)来确定变量类型。Java中,动态绑定通常用于虚方法(如非私有的实例方法等)接收者的类型绑定。
某些动态类型语言将动态绑定作为默认的内部实现。Java作为一种静态类型语言,采取了一些其他的方法来实现动态绑定(比如invokevirtual指令动态识别对象的实际类型)。
上面例子中,getAge()属于虚方法, getAge()方法的接收者(对象a)在此次访问中是动态绑定,因此这里对象a的类型尽管在编译期被标记为Person,最后在运行期会被invokevirtual指令重新确定为a的实际类型Man,并在Man中查找能够匹配符号引用中方法名和描述符的方法,因此”a.getAge()”调用的是a的实际类型Man的getAge方法。
Java的“静态多分派,动态单分派”
所谓的”静态多分派“概念: 由于java的静态分派需要同时考虑方法接收者和方法参数的静态类型,某种层度上而言是考虑了两种宗量,尽管没有涉及任何实际类型,依然可以从行为上勉强理解为”多分派“。
参考文献
①深入理解Java虚拟机
②http://hippo-jessy.com/2017/02/13/【深入理解Java虚拟机-1】Resolution-vs-Binding-vs-Dispatch/
原文地址:https://blog.csdn.net/u013309870/article/details/72991236
深入理解Jvm--Java静态分配和动态分配完全解析的更多相关文章
- 深入学习重点分析java基础---第一章:深入理解jvm(java虚拟机) 第一节 java内存模型及gc策略
身为一个java程序员如果只会使用而不知原理称其为初级java程序员,知晓原理而升中级.融会贯通则为高级 作为有一个有技术追求的人,应当利用业余时间及零碎时间了解原理 近期在看深入理解java虚拟机 ...
- 深入理解JVM—Java 6 JVM参数配置说明
原文地址:http://yhjhappy234.blog.163.com/blog/static/316328322011119111014657/ 使用说明< xmlnamespace pre ...
- 深入理解JVM - Java内存模型与线程 - 第十二章
Java内存模型 主内存与工作内存 Java内存模型主要目标:定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节.此处的变量(Variable)与Java编程中 ...
- 深入理解JVM - Java内存区域与内存溢出异常 - 第二章
一 运行时数据区域 JVM在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间. 程序计数器 程序计数器(Program Counter ...
- 深入理解JVM : Java垃圾收集器
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现. Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商.不同版本的虚拟机所提供的垃圾收集器都可能会有很大差 ...
- 理解JVM之内存分配以及分代思想实现
1.基本内存分批策略 大多数情况在新生代Eden区分配,如果启动了本地线程分配缓冲,将按线程优先在TLAB(线程私有缓冲区)上分配.当Eden区域没有足够的空间时将发起一次Minor GC. 值得注意 ...
- v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码
本篇关键词:池头.池体.节头.节块 内存管理相关篇为: v31.02 鸿蒙内核源码分析(内存规则) | 内存管理到底在管什么 v32.04 鸿蒙内核源码分析(物理内存) | 真实的可不一定精彩 v33 ...
- 【转】c语言动态与静态分配
https://blog.csdn.net/qq_43519310/article/details/85274836 https://blog.csdn.net/qq_38906523/article ...
- 深入理解JVM内幕:从基本结构到Java 7新特性
转自:http://www.importnew.com/1486.html 每个Java开发者都知道Java字节码是执行在JRE((Java Runtime Environment Java运行时环境 ...
随机推荐
- LeetCode169 Majority Element, LintCode47 Majority Number II, LeetCode229 Majority Element II, LintCode48 Majority Number III
LeetCode169. Majority Element Given an array of size n, find the majority element. The majority elem ...
- js多图上传展示和删除
html部分 <button class="btn btn-info" for="file">请选择文件</button> <in ...
- 【转载】CPU相关总结
What is the difference between Processor, Core, Logical Processor ? Processor : It’s the physical co ...
- 如何在云上使用confd+ACM管理敏感数据
在前面的一些文章中,我们介绍了如何在云上安全的存放配置数据,但是上面的方法都是有代码侵入性的,也就是说需要修改应用程序,本文会讲解如何使用 confd+ACM 在不修改代码的情况下动态修改应用所需的配 ...
- JAVA高级--java泛型
类型的参数化 泛型类可以同时设置多个参数 泛型类可以继承泛型类 泛型类可以实现泛型接口 示例--泛型类 package com.date; public class GenericDemo { pub ...
- 【Leetcode链表】反转链表 II(92)
题目 反转从位置 m 到 n 的链表.请使用一趟扫描完成反转. 说明: 1 ≤ m ≤ n ≤ 链表长度. 示例: 输入: 1->2->3->4->5->NULL, m ...
- 尖峰7月线上技术分享--Hadoop、MySQL
7月2号晚20:30-22:30 东大博士Dasight分享主题<大数据与Hadoop漫谈> 7月5号晚20:30-22:30 原支付宝MySQL首席DBA分享主题<MySQL ...
- 机房收费系统——技术总结 标签: vb数据库技术 2014-09-07 22:29 1153人阅读 评论(30)
说完了经验,再谈谈技术方面,其实对于技术,师哥师姐们都已经写了好多博客,这也是我在敲机房几乎没写这方面的博客的原因,不过别人的再好也是别人的,下面说说自己的. (一)数据库的设计 其实虽然说是数据库的 ...
- 运行docker容器镜像
docker容器可以理解为在盒中运行的进程. 这个盒包含了该进程运行所必须的资源,包括文件系统.系统类库.shell 环境等等. 但这个盒默认是不会运行任何程序的. 1.运行镜像之前,可以先查看本地有 ...
- SED总结, mac上要加备份文件名,sort命令和对中文的处理
使用sed批量改文件名 Sed批量去拓展名 |- dev.gb.conll06.raw |- test.gb.conll06.raw |- train.gb.conll06.raw 想要去掉其中的后缀 ...