什么';Java继承的大小成本是多少?

什么';Java继承的大小成本是多少?,java,oop,inheritance,jvm,internal-representation,Java,Oop,Inheritance,Jvm,Internal Representation,互联网上有各种各样的文章试图根据经验估算java.lang.Object在特定JVM实现中的开销。例如,我在一些JVM中看到了裸对象的大小开销 我想知道的是扩展关系的典型JVM实现是否会在类层次结构的每一层引入增量大小开销。换句话说,假设您有一个包含N个级别的子类的类层次结构。类实例的内存表示的开销是O(1)还是O(N) 我认为它是O(1),因为尽管一些隐藏的蓬松的东西的大小需要成为Java对象(vtable,类链)会随着继承层次结构的增长而增长,但它们是按类增长的,而不是按实例增长的,JVM实

互联网上有各种各样的文章试图根据经验估算
java.lang.Object
在特定JVM实现中的开销。例如,我在一些JVM中看到了裸
对象的大小开销

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

我认为它是O(1),因为尽管一些隐藏的蓬松的东西的大小需要成为Java
对象(vtable,类链)会随着继承层次结构的增长而增长,但它们是按类增长的,而不是按实例增长的,JVM实现可以将指向这些实体的恒定大小指针存储在附加到每个
对象的恒定大小头中

因此,理论上,对于继承深度N,直接附加到任何Java对象的内存表示的开销应该是O(1)。有人知道这在实践中是否正确吗

理论上,对于继承深度N,直接附加到任何Java对象的内存表示的开销应该是O(1)。有人知道这在实践中是否正确吗

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

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

因此,规范并不关心您是如何做到这一点的。但是

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

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

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

还有,关于

method\u info
结构表示此方法声明的所有方法 类或接口类型,包括实例方法、类方法、, 实例初始化方法(§2.9)和任何类或接口 初始化方法(§2.9)方法表不包括项 表示从超类或 超级界面。


Double和Integer,扩展数字,扩展对象,没有O(n)行为,也就是说,整数不是对象大小的3倍,所以我认为答案是O(1)。e、 g.参见

一个实例通常需要以下数据,尽管具体操作取决于实现:

  • 类及其父类的实例字段,我假设您不打算将其包含在术语“开销”中
  • 锁定对象的一些方法
  • 如果垃圾收集器重新定位了对象,那么可以使用一些方法来记录对象的原始散列(对于
    object.hashCode
  • 访问类型信息的一些方法
正如您在问题中所猜测的,在“普通”Java实现中,类型信息存储在每个类中,而不是每个实例中。“类型”的部分定义是,同一类的两个实例必然具有相同的类型信息,没有明显的理由不共享它。因此,您希望每个实例的开销是恒定的,而不依赖于类层次结构

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


顺便说一句,我的清单上的第二和第三件事可以通过巧妙的诡计组合在一起,这样它们就成为了一个指针。您链接到的文章引用了4个字节的引用,因此它为对象提供的8个字节是一个指向类型信息的指针,一个包含哈希代码或指向监视器的指针的字段,可能还有一个或两个指针字段的最低2位中的一些标志<在64位Java上,code>对象将(您期望的)更大。

如果有疑问,请查看(好的,源代码;每个JVM可以自由选择如何执行,因为标准不要求任何内部表示)。所以我看了一下,在JDK 7-u60的hotspot 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

按照我的理解,这意味着,对于这个(非常流行的)实现,对象实例只存储指向其类的指针具有较长或较短继承链的类的每个实例的成本实际上为0。这些类本身确实占用了内存空间(但每个类只占用一次)。深层继承链的运行时效率是另一个问题。

您试图解决什么问题?因为如果你想使用Java,你不能使用原语类型(如果可以的话,你应该使用原语类型);那你就剩下继承权了。我希望它是O(N),其中N是继承树的长度。使它成为O(N)有什么好处?我想不起来