Welcome everyone

从字节码看多态原理

java 汪明鑫 718浏览 0评论

先上代码,先睹为快

public abstract class A {
    public void print() {
        System.out.println("A print ...");
    }

    public abstract void sayHello();
}

public class B extends A {
    @Override
    public void sayHello() {
        System.out.println("hello ...");
    }
}

public class AbstractTest {
    public static void main(String[] args) throws IOException {
        A obj = new B();
        System.in.read();
        System.out.println(obj);
    }
}

上面的代码比较简单,直接跑起来

多态允许具体访问时实现方法的动态绑定。Java对于动态绑定的实现主要依赖于方法表。

本文我们从字节码角度看方法表,来看看Java底层怎么实现多态的

jps 看看刚才跑的java进程

~ » jps                                                  xinye@wangmingxin03
5380 
82150 LoopTest
13785 Launcher
14043 Jps
13789 AbstractTest
13775 KotlinCompileDaemon

13789

然后介绍一个Java 内置的工具

HSDB 全称是:Hotspot Debugger,是内置的 JVM 工具,可以用来深入分析 JVM 运行时的内部状态。

HSDB 位于 JDK 安装目录下的 lib/sa-jdi.jar 中, 启动 HSDB

/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib

sudo java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

找到我们B这个类

public class pers.wmx.springbootfreemarkerdemo.bytecode.B @0x00000007c0060c18

B的内存指针地址 0x00000007c0060c18

通过 Tools->Inspector 输入上面找到的地址找到 vtable, 就是方法表

可以看到它的 vtable 的长度为 7。先说结论:有 5 个是 上帝类 Object 的 5 个方法,一个是 B 覆写的 sayHello 方法,一个是继承 A 的 printMe 方法

vtable 分配在 instanceKlass 对象实例的内存末尾,instanceKlass大小在 64 位系统的大小为 0x1b8

注:0x开头的都是16进制

8 * 16^0 + 11 * 16^1 + 1 * 16^2 = 440

0x00000007c0060c18 + 0x1b8 = 0x7c0060dd0

定位到方法表

到console

mem 0x7c0060dd0 7

可以查看 vtable 内存起始地址的 7 个方法指针地址了

vtable 里存储的是指向方法内存的指针

前5个是继承于Object的方法,直接指向Object的方法

再看剩下2指针

继承于A的print方法
B自己实现sayHello方法

我们看下B的vtable图示:

vtable 是 Java 实现多态的基石,如果一个方法被继承和重写,会把 vtable 中指向父类的方法指针指向子类自己的实现。

  • Java 子类会继承父类的 vtable。Java 所有的类都会继承 java.lang.Object 类,Object 类有 5 个虚方法可以被继承和重写。当一个类不包含任何方法时,vtable 的长度也最小为 5,表示 Object 类的 5 个虚方法
  • final 和 static 修饰的方法不会被放到 vtable 方法表里
  • 当子类重写了父类方法,子类 vtable 原本指向父类的方法指针会被替换为子类的方法指针
  • 子类的 vtable 保持了父类的 vtable 的顺序

转载请注明:汪明鑫的个人博客 » 从字节码看多态原理

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz