Java继承的大小成本是多少?

0xb*_*7ed 52 java oop inheritance jvm internal-representation

在互联网上有各种各样的文章,试图通过经验估计java.lang.Object特定JVM实现的开销.例如,我在一些JVM中看到了一个Object 估计为8字节的裸大小开销.

我想知道的是,extends关系的典型JVM实现是否在类层次结构的每个级别引入增量大小开销.换句话说,假设您有一个具有N级子类的类层次结构.是类实例O(1)还是O(N)的内存中表示的开销?

我想它是O(1)因为虽然你需要成为Java Object(vtable,类链)的一些隐藏的蓬松东西的大小会随着继承层次结构的增长而增长,但它们每个类增长,而不是每个实例,JVM实现可以在连接到每个实体的常量大小的头中存储这些实体的常量大小的指针Object.

所以从理论上讲,直接附加到任何Java对象的内存中表示的开销应该是O(1)的继承深度N.有谁知道它在实践中是否正确?

Sot*_*lis 23

JVM规范说明

Java虚拟机不要求对象的任何特定内部结构.

因此规范并不关心你是如何做到的. ......

在Oracle的一些Java虚拟机实现中,对类实例的引用是指向句柄的指针,该句柄本身是一对指针:一个指向包含对象方法的表和指向表示Class对象的指针对象的类型,另一个是从堆为对象数据分配的内存.

因此,在典型的Oracle实现中,方法是O(1).此方法表是每个类的方法区域.

Java虚拟机具有在所有Java虚拟机线程之间共享的方法区域.方法区域类似于传统语言的编译代码的存储区域或类似于操作系统进程中的"文本"段.它存储每类结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法(第2.9节).

另外,关于方法条目

method_info结构表示由这个类或接口类型声明的所有方法,包括实例方法,类方法,例如初始化方法(§2.9),以及任何类或接口初始化方法(§2.9).methods表不包括表示从超类或超接口继承的方法的项.

  • 值得注意的是,尽管继承没有内存成本,但序列化形式还有其他数据,因为它描述了对象的整个类型层次结构. (2认同)

tuc*_*uxi 23

如有疑问,请查看(当然,一个源;每个JVM可以自由选择如何做到这一点,因为标准不强制要求任何内部表示).所以我看一下,在JDK 7-u60的热点JVM的实现中发现了以下注释:

// A Klass is the part of the klassOop that provides:
//  1: language level class object (method dictionary etc.)
//  2: provide vm dispatch behavior for the object
// Both functions are combined into one C++ class. The toplevel class "Klass"
// implements purpose 1 whereas all subclasses provide extra virtual functions
// for purpose 2.

// One reason for the oop/klass dichotomy in the implementation is
// that we don't want a C++ vtbl pointer in every object.  Thus,
// normal oops don't have any virtual functions.  Instead, they
// forward all "virtual" functions to their klass, which does have
// a vtbl and does the C++ dispatch depending on the object's
Run Code Online (Sandbox Code Playgroud)

我读它的方式,这意味着,对于这个(非常流行的)实现,对象实例只存储指向其类的指针.具有更长或更短的继承链的类的每个实例的成本实际上为0.这些类本身确实占用了内存空间(但每个类只有一次).深度继承链的运行时效率是另一回事.

  • @Bakuriu:多年前我们(我的同事和我)发现我们实际上*需要*一个'instanceof`的技巧,虽然我们没有使用那个确切的技巧.原因是我们不想在异常处理期间计算`instanceof`时耗尽堆栈.这对于单继承来说不是问题,使用固定堆栈的循环会这样做.但是多重继承(接口)意味着在最坏的情况下,简单的树搜索需要很多堆栈.我不知道Sun当时做了什么:我们与他们的许可意味着我们只能在受控制的情况下对他们的代码进行抄袭. (2认同)