Javainheritance的大小成本是多less?

在interwebs上有各种各样的文章,试图在实践中估计特定JVM实现中java.lang.Object的开销。 例如,我已经看到在一些JVM中, 估计为8字节的裸Object的大小开销。

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

我想这是O(1),因为虽然一些隐藏的蓬松的东西的大小,你需要是一个Java Object (Vtable,类的链)将随着inheritance层次增长而增长,他们增长每类,实例,并且JVM实现可以在连接到每个Object的常量大小头中存储指向这些实体的常量大小的指针。

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

如果有疑问,请查看源代码 (当然,每个JVM可以自由select如何执行,因为标准没有强制任何内部表示)。 于是我看了一下,在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 

我读的方式,这意味着,对于这个(非常stream行的)实现,对象实例只存储一个指向他们的类的指针。 具有更长或更短inheritance链的类的每个实例的成本实际上是0 。 这些课程本身也占用了内存空间(但每class只有一次)。 深度inheritance链的运行效率是另一回事。

JVM规范说明

Java虚拟机没有为对象指定任何特定的内部结构。

所以规范不关心你如何做。 但是

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

所以在典型的Oracle实现中,方法是O(1)。 这个方法表是每个类的方法区域 。

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

另外,关于方法条目

method_info结构表示由此类或接口types声明的所有方法,包括实例方法,类方法,实例初始化方法(§2.9)以及任何类或接口初始化方法(§2.9)。 方法表不包含表示从超类或超接口inheritance的方法的项。

一个实例通常需要以下数据,但是实际上需要做什么:

  • 类和它的父类的实例字段,我假设你并不是要在术语“开销”
  • 一些手段来locking对象
  • 如果垃圾收集器重新定位对象,则有一些方法logging对象的原始散列( Object.hashCode
  • 一些手段来访问types信息

正如你在你的问题中猜测的那样,在一个“普通的”Java实现中,types信息按类存储,而不是按实例存储。 部分“types”的定义是同一类的两个实例必须具有相同的types信息,没有明显的理由不共享它。 所以你会期望每个实例的开销是不变的,而不是依赖于类层次结构。

也就是说,向类中添加额外的空类或接口不应该增加其实例的大小。 我不认为语言或JVM规范实际上都能保证这一点,所以不要对“非正常”的Java实现允许做什么做太多的假设。

顺便说一句,我列表中的第二和第三个东西可以通过狡猾的欺骗手段相结合,所以他们两个在一起是一个单一的指针。 你链接到的文章是指引用4个字节的引用,所以它为一个对象提供的8个字节是一个指向types信息的指针,一个字段包含哈希码指向监视器的指针,可能有一些标志位于最低2位指针字段中的一个或两个。 在64位Java上, Object会(你会希望的)更大。

Double和Integer扩展了Number,扩展了Object,没有O(n)的行为,也就是说整数不是Object的大小的3倍,所以我认为答案是O(1)。 例如看到这个老的SO问题

在理论上,任何Java对象的内存中表示直接附加的开销应该是O(1)的inheritance深度N.有谁知道它是否在实践中是真的?

除非每个级别都有零个实例成员,否则不能是O(1)。 每个实例成员每个实例需要空间。