Java对象内存占用 - Visualvm和java.sizeOf测量

Aur*_*ien 8 java memory visualvm object

我试图了解Java中对象的内存占用量.我在Java中阅读了关于对象和内存的这个和其他文档.

但是,当我使用sizeof Java库或visualvm时,我会得到两个不同的结果,根据之前的参考文献(http://www.javamex.com),这些结果都不会超出我的预期.

对于我的测试中,我使用Java SE 7 Developer Preview一个64-bits Mac带有java.sizeof 0.2.1visualvm 1.3.5.

我有三个班,TestObject,TestObject2,TestObject3.

public class TestObject
{

}

public class TestObject2 extends TestObject
{
    int a = 3;
}

public class TestObject3 extends TestObject2
{
    int b = 4;
    int c = 5;
}
Run Code Online (Sandbox Code Playgroud)

我的主要课程:

public class memoryTester
{
    public static void main(String[] args) throws Throwable
    {
        TestObject object1 = new TestObject();
        TestObject2 object2 = new TestObject2();
        TestObject3 object3 = new TestObject3();

        int sum = object2.a + object3.b + object3.c;
        System.out.println(sum);

        SizeOf.turnOnDebug();

        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object1)));
        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object2)));
        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object3)));
    }
}
Run Code Online (Sandbox Code Playgroud)

使用java.SizeOf()我得到:

{ test.TestObject
} size = 16.0b
16.0b

{ test.TestObject2
 a = 3
} size = 16.0b
16.0b

{ test.TestObject3
 b = 4
 c = 5
} size = 24.0b
24.0b
Run Code Online (Sandbox Code Playgroud)

有了visualvm,我有:

this (Java frame)   TestObject  #1  16
this (Java frame)   TestObject2 #1  20
this (Java frame)   TestObject3 #1  28
Run Code Online (Sandbox Code Playgroud)

根据我通过Internet阅读的文档,因为我是64位,我应该有一个16字节的对象标题,可以TestObject.

然后TestObject2我应该为整数字段添加4个字节给出20个字节,我应该再添加4个字节的填充,总共大小为24个字节TestObject2.我错了吗?

继续这样TestObject3,我必须为两个整数字段添加8个字节,这应该给出32个字节.

VisualVm似乎忽略了填充,而java.sizeOf似乎错过了4个字节,好像它包含在对象头中一样.我可以用4个布尔值替换整数,它给出了相同的结果.

问题:

为什么这两个工具给出不同的结果?

我们应该填充吗?

我也读过某个地方(我没找到链接),在类和它的子类之间可能有一些填充,是不是?在这种情况下,继承的类树可能会有一些内存开销?

最后,是否有一些Java spec/doc详细说明了Java正在做什么?

谢谢你的帮助.

更新:

要回答utapyngo的注释,为了获得visualvm中对象的大小,我创建了一个heapdump,然后在"Classes"部分中,检查"实例"列之后的"size"列.每种对象的实例数,如果为1.

为了回答纳撒尼尔·福特的评论,我对每个人进行了初始化,然后在我的主要方法中用它们做了一个简单的总结来利用它们.它没有改变结果.

Chr*_*s K 2

是的,填充可能会发生。堆栈上的对象也有可能被完全优化。只有 JVM 知道任意时间点的确切大小。由于从 Java 语言内部估算大小的技术往往不一致,因此附加到 JVM 的工具往往是最准确的。据我所知,在 Java 中实现 sizeOf 的三种主要技术是:

  1. 序列化对象并返回这些字节的长度(显然是错误的,但对于相对比较很有用)
  2. 列表项反射,以及对象上找到的每个字段的硬编码大小常量。可以调整得有点准确,但是 JVM 中的更改以及 JVM 可能执行或不执行的填充都会抛出该错误。
  3. 列表项创建对象负载,运行 gc 并比较 jvm 堆大小的变化

这些技术都不准确。

如果您在 Oracle JVM 上运行,则 v1.5 或之后版本。然后有一种方法可以直接从 Java 运行时使用的 C 结构中读取对象的大小。对于生产来说这不是一个好主意,如果出错的话可能会导致 JVM 崩溃。但如果您想尝试一下,您可能会发现这篇博客文章很有趣:http ://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/

至于有关 Java 实际用途的文档,这是特定于 JVM、特定于版本以及可能特定于配置的。每个实现都可以自由地以不同的方式处理对象。例如,即使达到完全优化对象的程度,未从堆栈传递出的对象也可以自由地不在堆上分配。有些 JVM 甚至可能设法将对象完全保留在 CPU 寄存器中。这里不是你的情况,但我将其作为一个例子来说明为什么获取 Java 对象的真实大小很棘手。

因此,最好对您获得的任何 sizeOf 值持保留态度,并将其仅视为“指导”测量。