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

时间:2013-03-19 02:32:50

标签: java object memory visualvm

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

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

对于我的测试,我使用Java SE 7 Developer Preview 64-bits Macjava.sizeof 0.2.1visualvm 1.3.5使用TestObject

我有三个课程,TestObject2TestObject3public class TestObject { } public class TestObject2 extends TestObject { int a = 3; } public class TestObject3 extends TestObject2 { int b = 4; int c = 5; }

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)));
    }
}

我的主要课程:

{ 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

使用java.SizeOf()我得到:

this (Java frame)   TestObject  #1  16
this (Java frame)   TestObject2 #1  20
this (Java frame)   TestObject3 #1  28

使用visualvm我有:

TestObject

根据我在互联网上阅读的文件,因为我是64位的,我应该有一个16字节的对象标题,对TestObject2来说没问题。

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

继续{{1}}的方式,我必须为两个整数字段添加8个字节,这应该给出32个字节。

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

问题:

为什么这两个工具会产生不同的结果?

我们应该填充吗?

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

最后,是否有一些Java规范/文档详细说明了Java正在做什么?

感谢您的帮助。

更新

要回答utapyngo的评论,为了获得visualvm中对象的大小,我创建了一个heapdump,然后在" Classes"第一部分检查专栏"尺寸"列"实例"之后的下一个。每种对象的实例数,如果为1。

为了回答纳撒尼尔·福特的评论,我初步化了每个玩家,然后在我的主要方法中与他们做了一个简单的总结,以利用它们。它并没有改变结果。

1 个答案:

答案 0 :(得分:2)

是填充可能发生。堆栈上的对象也可以完全优化。只有JVM知道任何时间点的确切大小。由于这种从Java语言中近似大小的技术都倾向于不同意,因此附加到JVM的工具往往是最准确的。我所知道的在Java中实现sizeOf的三种主要技术是:

  1. 序列化对象并返回这些字节的长度 (显然是错误的,但对相对比较很有用)
  2. 列出项目反射, 和在对象上找到的每个字段的硬编码大小常量。能够 被调整为有点准确但在JVM和填充中的变化 JVM可能会或可能不会执行它将抛出它。
  3. 列出项目创建加载 对象,运行gc并比较jvm堆大小的变化
  4. 这些技巧都不准确。

    如果您在v1.5上或之后在Oracle JVM上运行。然后有一种方法可以直接从Java运行时使用的C结构中读取对象的大小。对于生产来说不是一个好主意,并且错误然后你可以崩溃JVM。但是这里有一篇博文,如果你想了解它,你可能会感兴趣:http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/

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

    所以最好采取任何尺寸的价值,你得到一点点盐,并把它作为一个指南'仅测量。