• 【JVM】字节码技术:手撕 多态执行原理


    一、源文件

    package org.example.classLoading;
    
    import java.io.IOException;
    /**
     * 演示多态原理,注意加上下面的 JVM 参数,禁用指针压缩
     * -XX:-UseCompressedOops -XX:-UseCompressedClassPointers
     */
    public class PolymorphicTest {
        public static void test(Animal animal){
            animal.eat();
        }
        public static void main(String[] args) throws IOException {
            test(new Cat());
            test(new Dog());
            System.in.read();
        }
    }
    
    abstract class Animal {
        public abstract void eat();
    
        @Override
        public String toString() {
            return "我是"+this.getClass().getSimpleName();
        }
    }
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("啃骨头");
        }
    }
    class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("吃鱼");
        }
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    二、运行代码

    停在 System.in.read() 方法上,这时运行 jps 获取进程 id:4268

    PS F:\software\IDEA\JavaProjects\JvmTest> jps
    5956 Launcher
    10956
    17596 Jps
    4268 PolymorphicTest
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三、运行HSDB工具

    进入 JDK 安装目录,执行

    F:\software\Java\jdk1.8.0_333>java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    
    • 1

    进入图形界面 attach 进程 id

    在这里插入图片描述

    四、查找某个对象

    打开 Tools -> Find Object By Query

    输入 select d from org.example.classLoading.Dog d 点击 Execute 执行

    在这里插入图片描述

    五、查看对象内存结构

    点击 0x0000020f086acd98 可以看到对象的内存结构。此对象没有任何属性,因此只有对象头的 16 字节

    前 8 字节是 MarkWord,后 8 字节就是对象的 Class指针 。目前看不到 Class对象 的实际地址

    在这里插入图片描述

    六、查看对象Class的内存地址

    方式一:连接前面第五节

    可以通过 Windows -> Console 进入命令行模式,执行下面命令查找对象中word的内存地址

    mem 0x0000020f086acd98 2
    
    • 1

    命令中有两个参数。参数 1 是对象地址,参数 2 是查看 2 行(即 16 字节)

    结果中第二行 0x0000020f373d4128 即为 Class对象 的内存地址

    在这里插入图片描述

    方式二

    Tools -> Class Browser 输入 Dog 查找,可以得到相同的结果:0x0000020f373d4128

    在这里插入图片描述

    七、查看类的多态方法表

    Alt+R 进入 Inspector 工具,输入刚才的 Class对象 内存地址,看到如下界面

    在这里插入图片描述

    无论通过哪种方法,都可以找到 Dog Class对象 内存地址。发现 vtable 长度为 6,意思就是 Dog 类有 6 个虚方法(多态相关的。final,static 不会列入)

    那么这 6 个方法都是谁呢?从 Class 的起始地址开始算,偏移 0x1b8 就是 vtable 的起始地址,进行计算得到:

    0x0000020f373d4128
    			   1b8 +
    --------------------
    0x0000020f373d42e0
    
    • 1
    • 2
    • 3
    • 4

    通过 Windows -> Console 进入命令行模式,执行下面命令。得到了 6 个虚方法的入口地址

    mem 0x0000020f373d42e0 6
    
    • 1

    在这里插入图片描述

    八、验证方法地址

    通过 Tools -> Class Browser 查看每个类的方法定义,比较可知:

    1. Dog - public void eat() @0x0000020f373d40d0

      在这里插入图片描述

    2. Animal - public java.lang.String toString() @0x0000020f373d36a8

      在这里插入图片描述

    3. Object - protected void finalize() @0x0000020f36fd1b10

    4. Object - public boolean equals(java.lang.Object) @0x0000020f36fd15e8

    5. Object - public native int hashCode() @0x0000020f36fd1540

    6. Object - protected native java.lang.Object clone() @0x0000020f36fd1678

      在这里插入图片描述

    九、小结

    当执行 invokevirtual 指令时

    1. 先通过栈帧中的对象引用找到对象
    2. 分析对象头,找到对象的 实际Class
    3. Class 结构中有 vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了
    4. 查vtable表 得到方法的具体地址
    5. 执行方法的字节码
  • 相关阅读:
    【操作系统】操作系统笔记
    7、Netty核心模块API
    LeetCode 1159.市场分析2
    Yarn 配置管理
    常见的SSH功能
    短视频(批量剪辑+矩阵发布+爆款文案+无人直播)一体化营销工具
    4年工作经验,多线程间的5种通信方式都说不出来,你信吗?
    Nginx错误日志说明
    《Spring Security 简易速速上手小册》第1章 Spring Security 概述(2024 最新版)
    基于Spring Boot+MyBatis+MySQL的高校试卷管理系统
  • 原文地址:https://blog.csdn.net/weixin_43401592/article/details/128019201