在互联网上有各种各样的文章试图在特定的JVM实现中凭经验估计java.lang.Object
的开销。例如,我在一些JVM中看到了裸Object
estimated at 8 bytes的大小开销。
我想知道的是extends
关系的典型JVM实现是否会在类层次结构的每个级别引入增量大小开销。换句话说,假设您有一个具有N级子类的类层次结构。是类实例O(1)还是O(N)?
我想它是O(1)因为虽然你需要成为Java Object
(vtable,类链)的一些隐藏的蓬松东西的大小会随着继承层次结构的增长而增长,但它们会增长每个类,而不是每个实例,JVM实现可以在连接到每个Object
的常量大小的头中存储这些实体的常量大小指针。
因此从理论上讲,直接附加到任何Java对象的内存中表示的开销对于继承深度N应该是O(1)。有人知道它在实践中是否正确?
答案 0 :(得分:23)
Java虚拟机不强制要求任何特定的内部 对象的结构。
因此规范并不关心你是如何做到的。 但 ...
在Oracle的一些Java虚拟机实现中,a 对类实例的引用是指向自身句柄的指针 一对指针:一对包含方法的表 object和指向Class对象的指针,表示类型 对象,另一个是从堆分配的内存 对象数据。
因此,在典型的Oracle实现中,方法是O(1)。此方法表是每个类的Method Area。
Java虚拟机有一个在所有人之间共享的方法区域 Java虚拟机线程。方法区域类似于 存储区域用于传统语言或类似的编译代码 到操作系统进程中的“文本”段。它存储 每类结构,如运行时常量池,字段和 方法数据,以及方法和构造函数的代码,包括 在类和实例初始化和中使用的特殊方法(第2.9节) 接口初始化。
此外,关于method entries
method_info
结构表示由此声明的所有方法 类或接口类型,包括实例方法,类方法, 实例初始化方法(第2.9节),以及任何类或接口 初始化方法(§2.9)。 方法表不包含项目 表示从超类继承的方法 超接口。强>
答案 1 :(得分:22)
如果有疑问,请查看source(以及 a 来源;每个JVM都可以自由选择如何操作,因为标准不强制要求任何内部表示)。所以我看一下,在JDK 7-u60的热点JVM的实现中找到了following comment:
// 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 。这些类本身确实占用了内存空间(但每个类只有一次)。深度继承链的运行时效率是另一回事。
答案 2 :(得分:4)
一个实例通常需要以下数据,尽管它完全取决于实现:
Object.hashCode
)正如您在问题中所猜测的那样,在正常情况下,#34; Java实现类型信息是按类存储的,而不是每个实例存储的。 " type"的定义的一部分;是同一类的两个实例必然具有相同的类型信息,没有明显的理由不共享它。所以你会期望每个实例的开销是恒定的,而不是依赖于类层次结构。
也就是说,向类中添加额外的空类或接口不应该增加其实例的大小。我不认为语言或JVM规范实际上保证了这一点,所以不要对什么是非正常的"做出太多的假设。允许Java实现。
顺便说一下,我列表中的第二个和第三个东西可以通过狡猾的技巧组合在一起,这样它们就可以组成一个指针。你链接的文章是指引用4个字节的引用,因此它为对象提供的8个字节是一个指向类型信息的指针,一个字段包含 哈希码或指向监视器的指针,可能是这些指针字段中的一个或两个的最低2位中的一些标志。 Object
将(您预期)在64位Java上更大。
答案 3 :(得分:2)
Double和Integer,它扩展了Number,它扩展了Object,没有O(n)行为,也就是说,Integer的大小不是Object的3倍,所以我认为答案是O(1)。例如见this old SO question
答案 4 :(得分:1)
理论上,直接附加到任何Java对象的内存中表示的开销对于继承深度N应该是O(1)。有人知道它在实践中是否正确?
除非每个级别都有零实例成员,否则它不能是O(1)。每个实例成员每个实例都需要空间。