准确测量物体尺寸

时间:2013-02-20 13:31:56

标签: java memory jvm

这个问题底部的代码有点长,但基本上会创建一些对象并确定它们在内存中的大小。我使用以下JVM参数执行代码(TLAB以避免块内存分配,并且可以获得准确的内存使用数字):

-server -Xms2000m -Xmx2000m -verbose:gc -XX:-UseTLAB

我在64位Hotspot JVM上运行代码并获得以下输出:

  

Java HotSpot(TM)64位服务器VM
  对象:16个字节

     

具有1 int的对象:16个字节
  具有2个整数的对象:24个字节
  具有3个整数的对象:24个字节

     

1长的对象:24个字节
  具有2个长的对象:32个字节
  具有3个long的对象:40个字节

     

带有1个引用的对象:16个字节
  具有2个引用的对象:24个字节
  具有3个引用的对象:24个字节

我的结论是:

  • 一个对象需要12个字节,对齐到16个字节。
  • 一个int需要4个字节(1个对象,一个int是12 + 4 =仍然是16个字节,有2个int:12 + 8 = 20对齐到24个字节)
  • long需要8个字节(1个对象,其中一个long为12 + 8 = 20个字节,对齐为24个字节)

但我很难理解为什么引用不会使用与long一样多的空间。

由于64位JVM上的引用是8个字节,因此明显的结论是测量方法存在缺陷*。 你能解释一下发生了什么以及可以采取哪些措施来解决它?

<子> *注:
- 测量期间没有GC运行。
- 使用Netbeans分析器产生类似的结果。

public class TestMemoryReference {

    private static final int SIZE = 100_000;
    private static Runnable r;
    private static Object o = new Object();
    private static Object o1 = new Object();
    private static Object o2 = new Object();
    private static Object o3 = new Object();

    public static class ObjectWith1Int  { int i; }
    public static class ObjectWith2Ints { int i, j; }
    public static class ObjectWith3Ints { int i, j, k; }
    public static class ObjectWith1Long  { long i; }
    public static class ObjectWith2Longs { long i, j; }
    public static class ObjectWith3Longs { long i, j, k; }
    public static class ObjectWith1Object  { Object o = o1; }
    public static class ObjectWith2Objects { Object o = o1; Object p = o2; }
    public static class ObjectWith3Objects { Object o = o1; Object p = o2; Object q = o3; }

    private static void test(Runnable r, String name, int numberOfObjects) {
        long mem = Runtime.getRuntime().freeMemory();
        r.run();
        System.out.println(name + ":" + (mem - Runtime.getRuntime().freeMemory()) / numberOfObjects + " bytes  ");
    }

    public static void main(String[] args) throws Exception {
        System.out.println(System.getProperty("java.vm.name") + "  ");

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object(); } };
        test(r, "Object", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Int(); } };
        test(r, "Object with 1 int", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Ints(); } };
        test(r, "Object with 2 ints", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Ints(); } };
        test(r, "Object with 3 ints", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Long(); } };
        test(r, "Object with 1 long", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Longs(); } };
        test(r, "Object with 2 longs", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Longs(); } };
        test(r, "Object with 3 longs", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Object(); } };
        test(r, "Object with 1 reference", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Objects(); } };
        test(r, "Object with 2 references", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Objects(); } };
        test(r, "Object with 3 references", SIZE);
    }
}

2 个答案:

答案 0 :(得分:5)

  

因为64位JVM上的引用是8个字节

这是你可能存在缺陷的假设。

HotSpot能够使用"compressed oops"为JVM的某些位置的引用使用32位值(强调我的):

  

哪些oops被压缩了?

     

在ILP32模式JVM中,或者如果在LP64模式下关闭UseCompressedOops标志,则所有oops都是本机机器字大小。

     

如果UseCompressedOops为true,则将压缩堆中的以下oops:

     
      
  • 每个对象的klass字段
  •   
  • 每个oop实例字段
  •   
  • oop数组的每个元素(objArray)
  •   

怀疑这就是你的情况。

使用

进行测试
-XX:-UseCompressedOops

-XX:+UseCompressedOops

在我的机器上,默认情况下我会得到与您相同的结果,但是-XX:-UseCompressedOops我看到了:

Object:16 bytes
Object with 1 int:24 bytes
Object with 2 ints:24 bytes
Object with 3 ints:32 bytes
Object with 1 long:24 bytes
Object with 2 longs:32 bytes
Object with 3 longs:40 bytes
Object with 1 reference:24 bytes
Object with 2 references:32 bytes
Object with 3 references:40 bytes

......这可能更接近你的预期:)

答案 1 :(得分:0)

定义类时,Java对象的大小是已知的。

Java对象的内存使用情况:常规http://www.javamex.com/tutorials/memory/object_memory_usage.shtml