JVM的多态是如何实现的

简介: 哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。

哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。

手撸过JVM、内存池、垃圾回收算法、synchronized、线程池、NIO、三色标记算法…

erafbbd8u7.png

最近写文章有点缺课题,小伙伴们可以把在面试中被虐的面试题留言给子牙老师。虐得越惨越痛的那种,子牙老师不喜欢研究没有挑战性的课题。自信有木有?

昨天就有一个小伙伴被一道面试题虐了,我也给了他一定深度的答案。但是我觉得不够,我觉得应该让小伙伴们像我一样,答题能答出惊喜感,于是就有了这篇文章。我会从Java层面到Hotshot源码层面再到C++层面,完整分析这个问题。

这道面试题在好一些的互联网公司,尤其是一二线,问到的概率非常大,建议小伙伴们把这篇文章吃透。
image.png

这样说,六十分

多态是面向对象的三大特性之一,我个人认为,当时设计OOP机制的时候,能够想到多态的人,真特么太牛叉了。

多态理论第一次有了具体实现是在第一款面向对象的编程语言中,这个语言可能很多人没听过:smalltalk。此后出现的只要具备OOP机制的语言,都或多或少模仿或借鉴了前面语言的OOP实现机制。C++有没有模仿或借鉴smalltalk,我不敢说,没特别研究过smalltalk。但是我敢说,Java的多态是几乎百分百模仿C++的多态实现的,不过做了一些细化。C++中只有直接调用、间接调用,而JVM通过不同的invoke指令来实现不同属性的方法调用,这点后文会讲到。

那什么是多态呢,满足下面这几个条件就可以称为多态:
1、继承了某个类、实现了某个接口
2、重写父类的方法、实现接口中的方法
3、父类引用指向子类对象
image.png

其实面试官问的这个问题,你这样回答也算就着他这个问题做了回答。但是显然,面试官想听的不是这些,而是父类引用指向子类对象,进行方法调用,这个JVM底层是如何实现的。面试题就是为了筛人,所以面试的时候,能答多深就答多深,绝对加分。

顺便说下,经常跟多态联系在一起的两个词:动态绑定、晚绑定。别到时面试官说这两个词,你一脸懵,那真的很掉分,面试官的脸色一下就暗淡灰沉下去了。当面试官看到你的第一眼,心里给了你60分钟时间来表现,这下直接掉到5分钟。更直接一点的,可能找个借口就走人了。

这样说,七八十分

C++中的间接调用与直接调用,JVM抽象成了4个指令来完成:
1、invokevirtual:咱们平时写代码调用方法,最常用的就是这个指令。这个指令用于调用public、protected修饰,且不被static、final修饰的方法。跟多态机制有关。
2、invokeinterface:跟invokevirtual差不多。区别是多态调用时,如果父类引用是对象,就用invokevirtual。如果父类引用是接口,就用这个。
3、invokespecial:只用于调用私有方法,构造方法。跟多态机制无关。
4、invokestatic:只用于调用静态方法。与多态机制无关。

跟面试官当然要扯点高逼格的对吧,那咱们就讲讲invokeinterface。这个指令为什么逼格高呢?因为它的底层实现比其他几个指令都要复杂,如图
image.png

其他的invoke指令的后面就是2个字节的操作数,拿着操作数去常量池中就可以找到类信息、方法信息。但是invokeinterface你会发现,它后面操作数占了4个字节,这4个字节还不全是常量池索引,一起看下这个指令的结构,上图:
image.png

这个指令格式我解释一下:
1、第二个字节跟第三个字节合起来是常量池的索引,对应常量池项JVM_CONSTANT_InterfaceMethodref,这里面包含接口的元信息、方法信息。
2、第四个字节是这个方法的参数个数。是不是有小伙伴觉得很奇怪,show方法没有参数呀,这边怎么是1,是JVM的bug?呵,如果JVM有这么低级的bug,JVM也不会有今天的地位了。非静态方法就算没有参数,也默认有一个,就是this指针。其实这个参数个数完全没必要记录,可以通过解析方法的签名计算出来,不明白当时为什么做这样的设计。面试的时候这点记得说,很加分。其实字节码文件中有很多可以优化的点,后面准备共享这方面的面试题,没人想打我吧。^_^
image.png

3、第五个字节永远为0,历史原因遗留。我查了一些资料,得到的答案是:为额外的运算元预留空间。子牙老师表示这个字我都认识,但是它组合在一起表达的意思我真不懂,是不是我太菜了。哎,还是太菜了。

有些小伙伴可能就想:答到这个份上才七八十分?那后面还能怎么说哦。咱们现在才只说到invokeinterface指令,那这个指令是怎么找到要调用的方法的呢?JVM的虚表机制到底是什么样的呢?又是怎么与C++的虚表机制合二为一的呢?虚表分发机制又是怎样的呢?这些才是这个问题的精髓。

这样说,薪资随你开

不知不觉写了这么多了,这点就留到下篇文章写吧。文章太长,读起来也疲惫。

image.png

相关文章
|
Java 编译器 索引
|
4月前
|
Java 编译器 开发者
Java基础3-JVM层面理解Java继承、封装、多态的实现原理(二)
Java基础3-JVM层面理解Java继承、封装、多态的实现原理(二)
30 0
|
4月前
|
存储 Java 索引
Java基础3-JVM层面理解Java继承、封装、多态的实现原理(一)
Java基础3-JVM层面理解Java继承、封装、多态的实现原理(一)
35 0
|
存储 Java 编译器
深入分析java中的多态(从jvm角度分析)
对于java中多态概念的理解一直是面试常问的问题,所以今天花了一些时间好好地整理了一下,力求从java虚拟机的角度来分析和理解多态。
594 0
深入分析java中的多态(从jvm角度分析)
|
8天前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
23 4
|
2月前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。
|
8天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
29 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
11天前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
|
7天前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
33 2