众所周知,多态是面向对象编程语言的重要特性,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。C++ 和 Java 作为当前最为流行的两种面向对象编程语言,其内部对于多态的支持对于单继承的实现非常类似。

首先来体现一下C++的动态分派,如下:

class Base1{

public:
int base1_var1;
int base1_var2; void func(){}
};

C++中有函数的动态分派,就类似于Java中方法的多态。而C++实现动态分派主要就是通过虚函数来完成的,非虚函数在编译时就已经确定调用目标。C++中的虚函数通过关键字virtual来声明,如上函数func()没有virtual关键字,所以是非虚函数。

查看内存布局,如下:

1>  class Base1  size(8):
1> +---
1> 0 | base1_var1
1> 4 | base1_var2
1> +---

非虚函数不会影响内存布局。 

class Base1{

public:
int base1_var1;
int base1_var2; virtual void base1_fun1() {}
};

内存布局如下:

1>  class Base1  size(16):
1> +---
1> 0 | {vfptr}
1> 8 | base1_var1
1> 12 | base1_var2
1> +---

在64位环境下,指针占用8字节,而vfptr就是指向虚函数表(vtable)的指针,其类型为void**, 这说明它是一个void*指针。类似于在类Base1中定义了如下类似的伪代码:

void* vtable[1] = {  &Base1::base1_fun1  };

const void**  vfptr = &vtable[0];

这个非常类似于Java虚拟机中对Java方法动态分派时的虚函数表(可参看深入剖析Java虚拟机:源码剖析与实例详解》一书第6.3节)。

虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都使用同一个虚表。为了指定对象的虚表,对象内部包含一个虚表的指针,来指向自己所使用的虚表。为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针,*__vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

从如上的例子我们应该能够得到如下一些结论:

  1. C++的动态分派需要明确用virtual关键字指明,而Java中的方法默认就是动态分派,这也体现出两种语言设计理念的不同,C++不想让用户为用不到的功能付出代价,Java更看重开发效率,将一些复杂的底层控制全权托管给虚拟机;
  2. C++中只要有虚函数,就会在类中有一个虚表,而且类的每个实例都会多出一个指向虚表的指针。

对于第2点来说,HotSpot VM的设计者充分考虑了这个情况,所以在一些类的设计上能不用虚函数就绝对不用,例如oop继承体系下的所有类都不会用虚函数,因为有一个Java实例就会有一个oop,现在的应用程序一般都有过千万的实例,那我们可以算一下,每个实例要多出8个字节存储指向虚表的指针,那要消耗掉多少内存呢?!

下面我们来谈谈第1点提到的动态分派。先来看一下单继承情况下C++的动态分派。

class Person{
. . .
public :
void init(){} // 非virtual方法
virtual void sing (){};
virtual void dance (){};
}; class Girl : public Person{
. . .
public :
virtual void sing(){};
virtual void speak(){};
};

动态分派的过程大概如下图所示。



只有加virtual关键字的虚函数才会存在于虚函数表中,也就是这些虚函数需要动态分派。当子类重写了父类的方法时,动态分派能准确根据接收者类型找到实际需要调用的函数。

下面看一下Java的动态分派。举个例子如下:

class Person{
void sing (){}
void dance (){}
} class Girl extends Person{
void sing(){}
void speak(){}
}

了解过Java虚函数表的人可能知道,类中许多的方法都会放到虚函数表中,这就是我们前面说的,Java中的方法默认都是动态分派的。

动态分派的过程大概如下图所示。

从Object继承下来5个方法,final、静态方法和构造方法不会进入虚函数表中。JVM的方法调用指令有四个,分别是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前两个是静态绑定,后两个是动态绑定的,invokevirtual表示调用虚方法,也就是会查虚函数表进行调用,而invokeinterface表示调用接口方法,会有个接口函数表,这里暂不介绍。

本人最近准备出一个手写Hotspot VM的课程,超级硬核,从0开始写HotSpot VM,将HotSpot VM所有核心的实现全部走一遍,如感兴趣,速速入群。

群里可讨论虚拟机和Java性能剖析与故障诊断等话题,欢迎加入。

C++的动态分派在HotSpot VM中的重要应用的更多相关文章

  1. JVM详解之:HotSpot VM中的Intrinsic methods

    目录 简介 什么是Intrinsic Methods 内置方法的特点 多样性 兼容性 java语义的扩展 Hotspot VM中的内置方法 intrinsic方法和内联方法 intrinsic方法的实 ...

  2. HotSpot VM 中的JIT分类

    在HotSpot VM中内嵌有两个JIT编译器,分别为Client Compiler和Server Compiler,但大多数情况下我们简称为C1编译器和C2编译器.开发人员可以通过如下命令显式指定J ...

  3. 转:什么是即时编译(JIT)!?OpenJDK HotSpot VM剖析

    重点 应用程序可以选择一个适当的即时编译器来进行接近机器级的性能优化. 分层编译由五层编译构成. 分层编译提供了极好的启动性能,并指导编译的下一层编译器提供高性能优化. 提供即时编译相关诊断信息的JV ...

  4. [读书笔记]Java之动态分派

    以下内容来自周志明的<深入理解Java虚拟机>. 前一篇说了静态分派和重载有关,现在的动态分派就和覆盖Override有关了. 先看代码: public class DynamicDisp ...

  5. HotSpot VM运行时

    HotSpot VM运行时系统为HotSpot JIT编译器和垃圾收集器提供服务和通用API,同时还为VM提供启动.线程管理.JNI(Java本地接口)等基本功能.HotSpot VM运行时环境担当许 ...

  6. 关于HotSpot VM以及Java语言的动态编译 你可能想知道这些

    目录 1 HotSpot VM的历史 2 HotSpot VM 概述 2.1 编译器 2.2 解释器 2.3 解释型语言 VS 编译型语言 3 动态编译 3.1 什么是动态编译 3.2 HotSpot ...

  7. jvm中的动态分派

    动态分派与复写密不可分,因为java中存在向上转型,这样就涉及到方法的调用问题.先看一下示例代码 package com.dy.xidian; class Test1 { public void sa ...

  8. java中的静态分派和动态分派

    多态是java的基本特征之一,多态即一个对象具有多种形态(多种表达形式,猴子是动物的一种的表现形式),例如:子类是父类的一种形态. 当方法重载时,就会涉及到多态. 1:在重载时是通过参数的静态类型,而 ...

  9. 014-通过JDB调试,通过HSDB来查看HotSpot VM的运行时数据

    一.JDB调试        在预发环境下进行debug时,时常因为工具和环境的限制,导致debug体验非常差,那么有什么方法能够简化我们进行debug的体验吗?JDB就是一种.        JDB ...

  10. HotSpot VM GC 的种类

    collector种类 GC在 HotSpot VM 5.0里有四种: incremental (sometimes called train) low pause collector已被废弃,不在介 ...

随机推荐

  1. 【Ubuntu22.04】配置静态IP地址和FTP服务

    ## 一.配置静态IP 1. 使用命令`ip a`查看当前网卡名称,Ubuntu22.04默认网卡为ens33: ![](https://img2023.cnblogs.com/blog/308121 ...

  2. 基于php的外卖订餐网站(php+mysql)

    介绍 一个基于php的外卖订餐网站,包括前端和后台. 效果演示 http://101.43.124.118:8001/admin 源码地址 https://github.com/geeeeeeeek/ ...

  3. 自然语言处理 Paddle NLP - 预训练语言模型及应用

    什么是语言理解? 关于疫情的一段对话: 中国:我们这边快完了 欧洲:我们这边快完了 中国:我们好多了 欧洲:我们好多了 挑战: 语言的复杂性和多样性 多义/同义/歧义现象 灵活多变的表达形式 语言背后 ...

  4. AB实验:科学归因与增长的利器

    第一章 AB实验的基本原理和应用 AB实验的相关概念: 3个基本参数:实验参与单元.实验控制参数.实验指标 2个核心价值:验证因果关系.量化策略效果 2个关键特性:先验性.并行性 基本流程:分流 -& ...

  5. 5步带你玩转SpringBoot自定义自动配置那些知识点

    目前SpringBoot框架真的深受广大开发者喜爱,毕竟它最大的特点就是:快速构建基于Spring的应用程序的框架,而且它提供了各种默认的功能和配置,可以让开发者快速搭建应用程序的基础结构. 但是,当 ...

  6. GPT3的内部结构:基于自回归、注意力机制等技术的语言处理框架

    目录 1. 引言 2. 技术原理及概念 3. 实现步骤与流程 4. 应用示例与代码实现讲解 6. 结论与展望 7. 附录:常见问题与解答 GPT-3 是当前最为先进的自然语言处理框架之一,由 Open ...

  7. SQL SERVER 拼接字符串转化为表结构数据

    本文为一些需要对特殊符号分隔的字符串进行解析,比如将 select '10,20,30,40,50,60' 这个字符串转化为一列多行 下面提供源代码: 1 SET QUOTED_IDENTIFIER ...

  8. Django ORM:最全面的数据库处理指南

    深度探讨Django ORM的概念.基础使用.进阶操作以及详细解析在实际使用中如何处理数据库操作.同时,我们还讨论了模型深入理解,如何进行CRUD操作,并且深化理解到数据库迁移等高级主题.为了全面解读 ...

  9. 巧用 awk 批量杀进程

    今天遇到线上的一个问题: 我需要批量杀死某台机器的 PHP 进程,该怎么办? 注意,不是 php-fpm,是常驻任务. 如果是一个进程,那就好办了,ps -ef | grep php,找到 PID 然 ...

  10. 原生poi实现模版导出

    背景 我们公司是内网开发,外网jar包我的权限不够,所以easyexcel jar包无法使用,参考了easyexcel的填充思想,写了一个较简单的填充方法,如果直接用easyexcel的话,可以参考这 ...