一窥 HotSpot 方法表

作为 Java 多态实现的详细介绍 的补充。


现阶段对 OOP 的审视:

OOP 是什么?
面向对象编程。本质上就是对数据还有与之关联的行为进行编程。

面向对象的三大基本特征:①封装;②继承;③多态。

  • 封装:数据和行为的打包
  • 继承:优点为了实现多态缺点又是增加了耦合度。
  • 多态:接口和实现的解耦

解耦,解耦,解耦,还是TMD解耦。

设计模式的三个准则:
1)中意于组合而不是继承
2)依赖于接口而不是实现
3)高内聚,低耦合


instanceOop:平常我们所使用的 Java 实例在 JVM 中的表示。
instanceKlass:一个类所表示的元数据。
oop klass

64 位环境下使用 openjdk-8u40-src-b25-10_feb_2015获得 instanceKlass 的大小

instanceKlass size

与 HSDB 显示的一致:
这里写图片描述

// -XX:-UseCompressedOops
// 关闭指针压缩

public class Animal{
    public void say(){
        System.out.println("is animal");
    }
    public static void main(String[] args){
        Animal animal = new Dog();
        run(animal);
        animal = new Cat();
        run(animal);
    }
    public static void run(Animal animal){
        animal.say();
    }
}
class Dog extends Animal{
    public void say(){
        System.out.println("is Dog");
    }
}
class Cat extends Animal{
    public void say(){
        System.out.println("is Cat");
    }
}

当Hotspot 在运行期加载类 Animal 时,其 vtable 中将会有一个指针元素指向其 say 方法在 Hotspot 内部的内存首地址,当 Hotspot 加载类 Dog 时, 首先类 Dog 完全继承其父类 Animal 的 vtable,因此类 Dog 便也有一个 vtable ,并且 vtable 里有一个指针指向类 Animal 的 say方法的内存地址 。

Hotspot 遍历类 Dog 的所有方法,并发现say方法是 public 的,并且没有被 static 、 final 修饰,于是 HotSpot 去搜索其父类中名称相同、签名也相同的方法,结果发现父类中存在一个完全一样的方法,于是 HotSpot就会将类 Dog 的 vtable 中原本指向类 Animal 的 say方法的内存地址的指针值修改成指向类Dog 自己的say方法所在的内存地址 。


在该博主的总结:

  1. vtable 分配在 instanceKlass 对象实例的内存末尾 。
  2. 其实 vtable 可以看作是一个数组,数组中的每一项成员元素都是一个指针,指针指向 Java 方法在 JVM 内部所对应的 method 实例对象的内存首地址 。
  3. vtable 是 Java 实现面向对象的多态性的机制,如果一个 Java 方法可以被继承和重写, 则最终通过 put_method_at 函数将方法地址替换,完成 Java 方法的动态绑定。
  4. Java 子类会继承父类的 vtable,Java 中所有类都继承自 java.lang.Object, java .lang.Object 中有 5 个虚方法(可被继承和重写):
    void finalize()
    boolean equals(Object)
    String toString()
    int hashCode()
    Object clone()
    以上5个方法顺序是固定的。
    因此,如果一个 Java 类中不声明任何方法,则其 vtalbe 的长度默认为 5 。
  5. Java 类中不是每一个 Java 方法的内存地址都会保存到 vtable 表中,只有当 Java子类中声明的 Java 方法是 public 或者 protected 的,且没有 final 、 static 修饰,并且 Java 子类中的方法并非对父类方法的重写时, JVM 才会在 vtable 表中为该方法增加一个引用 。
  6. 如果 Java 子类某个方法重写了父类方法,则子类的vtable 中原本对父类方法的指针会被替换成子类对应的方法指针,调用 put_method_at 函数替换vtable 中对应的方法指针。

接下来,我们对原来那篇博文进行验证。

// -XX:-UseCompressedOops
// 关闭指针压缩
package test;

public class Main1 {
    public static void main(String[] args) {
        Number number = 10000;
    }
}

hsdb> universe
Heap Parameters:
Gen 0:   eden [0x0000000012400000,0x000000001268e6a8,0x00000000126b0000) space capacity = 2818048, 95.11860692223837 used
  from [0x00000000126b0000,0x00000000126b0000,0x0000000012700000) space capacity = 327680, 0.0 used
  to   [0x0000000012700000,0x0000000012700000,0x0000000012750000) space capacity = 327680, 0.0 usedInvocations: 0

Gen 1:   old  [0x0000000012750000,0x0000000012750000,0x0000000012e00000) space capacity = 7012352, 0.0 usedInvocations: 0

hsdb> scanoops 0x0000000012400000 0x00000000126b0000 java.lang.Integer
0x0000000012495470 java/lang/Integer --> -128
0x0000000012495488 java/lang/Integer --> -127
...
...
...
0x0000000012496c40 java/lang/Integer --> 126
0x0000000012496c58 java/lang/Integer --> 127
0x000000001263ee28 java/lang/Integer --> 255
0x000000001263fb00 java/lang/Integer --> 255
0x0000000012683518 java/lang/Integer --> 1000

可以观察到新生代一共创建了256个缓存 Integer 对象(-128-127),和 Integer.IntegerCache 类中的吻合。
2 个值为 255 的 Integer 对象。用处不得而知。
1 个 我们创建的值为 10000 的对象。

hsdb> whatis 0x0000000012683518
Address 0x0000000012683518: In thread-local allocation buffer for thread "main" (4)  [0x000000001267fdd0,0x0000000012683530,0x000000001268e690,{0x000000001268e6a8})

hsdb> inspect 0x0000000012683518
instance of Oop for java/lang/Integer @ 0x0000000012683518 @ 0x0000000012683518 (size = 24)
_mark: 1
_metadata._klass: InstanceKlass for java/lang/Integer
value: 10000
hsdb> mem 0x0000000012683518 3
0x0000000012683518: 0x0000000000000001 --> Mark Word
0x0000000012683520: 0x0000000012ebaee8 --> 类型指针
0x0000000012683528: 0x0000000000002710 --> 10000 对应的16进制表示

也可以观察到一个 java.lang.Integer 占用 24 个字节(一个字长也就是8字节的对象头,8字节的类型指针,4字节的int,4字节的填充确保实例是8字节的整数倍 )。
和 jol 结果一致。
jol

0x0000000012ebaee8 + 0x1b8 = 0x0000000012ebb0a0

0x0000000012ebb0a0 即为 vtable 的内存首地址。
这里写图片描述

可以看到 Integer 方法表中索引为 3 的引用指向Integer.toString()

再来验证下 java.lang.Number 的方法表的大小。
这里写图片描述
0x0000000012eb1a00 + 0x1b8 = 0x0000000012eb1bb8
这里写图片描述
经过测试,虚表的结束是以2个字长的空数据为结尾,所以可以断定Number的虚表大小为11。

参考:
http://www.cnblogs.com/2014asm/p/8633660.html


这个世界只有适合不适合的东西,不会出现放之四海皆准的东西,也不可能出现一种可以解决所有问题的东西,如果有,那么这种东西必然是一种宗教性质的用来洗脑的东西。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值