这完全取决于实现,但有一些因素会影响Java对象的大小。首先,Java对象中字段的数量和类型肯定会影响空间使用,因为您需要至少有足够的存储空间来容纳所有对象的字段。然而,由于填充、对齐和指针压缩优化,没有直接的公式可以用来精确计算以这种方式使用了多少空间。
至于方法,通常情况下对象中的方法数量对其大小没有影响。方法通常使用称为
虚函数表(或“vtable”)的特性实现,使得可以通过基类引用在恒定时间内调用方法。这些表通常通过在多个对象之间共享单个vtable实例,然后使每个对象存储单个指向vtable的指针来存储。
接口方法会稍微复杂一些,因为有几种不同的实现可能性。一种实现为每个接口添加一个新的vtable指针,因此实现的接口数量可能会影响对象大小,而其他实现则不会。同样,实际上如何在内存中组合这些东西是由实现决定的,因此您无法确定这是否会产生内存成本。
据我所知,目前不存在JVM的实现,其中方法的长度会影响对象的大小。通常情况下,每个方法仅存储一份内存,然后该代码跨所有特定对象的实例共享。较长的方法可能需要更多的总内存,但不应对类的实例的每个对象的内存产生影响。也就是说,JVM规范并没有承诺这必须是正确的,但我无法想到任何合理的实现方式,它会为了方法代码而为每个对象分配额外的空间。
除了字段和方法之外,许多其他因素可能会导致对象的大小增加。以下是一些例子:
根据JVM使用的垃圾收集器(或收集器)类型,每个对象可能会有额外的存储空间来保存信息,例如对象是否处于活动、死亡、可达等状态。这可以增加存储空间,但您无法控制。在某些情况下,JVM可能通过尝试将对象存储在堆栈而不是堆上来优化对象大小。在这种情况下,某些类型的对象甚至可能不存在开销。
如果使用同步,对象可能会分配额外的空间,以便可以对其进行同步。JVM的某些实现直到必要时才为对象创建监视器,因此如果您不使用同步,则可能会拥有更小的对象,但无法保证这种情况。
此外,为了支持像instanceof和类型转换之类的操作符,每个对象可能都有一些空间用于保留类型信息。通常,这与对象的vtable捆绑在一起,但不能保证这一点。
如果你使用断言,一些JVM实现将在你的类中创建一个字段,其中包含断言是否已启用。然后在运行时使用它来禁用或启用断言。再次强调,这是与实现相关的,但记住这一点很好。
如果你的类是一个非静态内部类,它可能需要持有对包含它的类的引用,以便可以访问它的字段。但是,如果从未使用它,则JVM可能会优化掉它。
如果您使用匿名内部类,则该类可能需要有额外的空间来保留在其封闭范围内可见的final变量,以便可以在类内部引用它们。这是与实现相关的,无论是将此信息复制到类字段中还是仅在堆栈上本地存储,都可能增加对象大小。
某些实现Object.hashCode()或System.identityHashCode(Object)可能需要在每个包含该哈希码值的对象中存储额外的信息,如果无法以其他方式计算它(例如,如果对象可以在内存中重定位)。这可能会增加每个对象的大小。