是的Dalvik甚至更多的内存中对象的尺寸方面比饿热点? [英] Is Dalvik even more memory hungry than HotSpot in terms of object sizes?

查看:104
本文介绍了是的Dalvik甚至更多的内存中对象的尺寸方面比饿热点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直想知道有多少内存对象占用的Andr​​oid。 有许多资源(如)相关热点JVM告诉了一个空的对象,需要8个字节 空数组12个字节,并且所有的对象被对准以8字节边界。 这样的对象,没有额外的领域应该采取8个字节,至少有一个额外的域最小的对象 - 16字节,一个空数组 - ?16个字节,右

I've been wondering how much memory does an Object occupy on Android. There are numerous resources (like this) related to HotSpot JVM telling that an empty object takes 8 bytes and an empty array 12 bytes and that all objects are aligned to 8 byte boundary. Thus an object with no extra fields should take 8 bytes, the smallest object with at least one extra field – 16 bytes, an empty array – 16 bytes, right?

我发现没有关于Dalvik的具体信息在这个问题上,决定弄清楚通过测试。 运行测试过的令人惊讶的结果

I've found no specific information about Dalvik on this matter and decided to figure it out by testing. Running the test had surprising results.

有关计算方法几句话。 Android的实施Object.hash code()只返回指针铸造为int的对象。 (似乎是显而易见的,一般情况下,但[又一个惊喜]事实证明,它并没有对热点的JVM实例 - 与热点运行MemTest这个,看看)。 所以,我使用的哈希值code()上的Dalvik简单通过分配在一个行和分配的空间量的测试类的两个实例来计算在Android上的物体的大小应该等于的差异他们的哈希code()值(假设这是毫无意义的Dalvik分配那些完全随机的地址)。只是要确保我在每次测试类行,其​​总交付哈希code相同的差异始终分配4个对象()。所以,我相信这是对方法的正确性毫无疑问的。

Few words about the method of calculation. Android's implementation of Object.hashCode() simply returns the pointer to the object casted to int. (seemed obvious and general, but [another surprise] as it turned out, it does NOT on HotSpot JVM for instance – run MemTest with HotSpot and see). So, I've used the simplicity of hashCode() on Dalvik to calculate the object size on Android by allocating two instances of the tested class in a row and the amount of the allocated space should be equal to the difference of their hashCode() values (assuming that it makes little sense for Dalvik to allocate those at completely random addresses). Just to be sure I've allocated always 4 objects in a row per test class, which delivered always the same difference of hashCode(). So, I believe there is little doubt about correctness of the method.

下面是测试的源$ C ​​$ C:

Here is the source code of the test:

public class MemTest {
    public static void run() {
        Object o1 = new Object();
        Object o2 = new Object();
        Object o3 = new Object();
        Object o4 = new Object();

        EmptyObject eo1 = new EmptyObject();
        EmptyObject eo2 = new EmptyObject();
        EmptyObject eo3 = new EmptyObject();
        EmptyObject eo4 = new EmptyObject();

        ObjectWithBoolean ob1 = new ObjectWithBoolean();
        ObjectWithBoolean ob2 = new ObjectWithBoolean();
        ObjectWithBoolean ob3 = new ObjectWithBoolean();
        ObjectWithBoolean ob4 = new ObjectWithBoolean();

        ObjectWithBooleanAndInt obi1 = new ObjectWithBooleanAndInt();
        ObjectWithBooleanAndInt obi2 = new ObjectWithBooleanAndInt();
        ObjectWithBooleanAndInt obi3 = new ObjectWithBooleanAndInt();
        ObjectWithBooleanAndInt obi4 = new ObjectWithBooleanAndInt();

        ObjectWithLong ol1 = new ObjectWithLong();
        ObjectWithLong ol2 = new ObjectWithLong();
        ObjectWithLong ol3 = new ObjectWithLong();
        ObjectWithLong ol4 = new ObjectWithLong();

        ObjectWith4Ints o4i1 = new ObjectWith4Ints();
        ObjectWith4Ints o4i2 = new ObjectWith4Ints();
        ObjectWith4Ints o4i3 = new ObjectWith4Ints();
        ObjectWith4Ints o4i4 = new ObjectWith4Ints();

        ObjectWith4IntsAndByte o4ib1 = new ObjectWith4IntsAndByte();
        ObjectWith4IntsAndByte o4ib2 = new ObjectWith4IntsAndByte();
        ObjectWith4IntsAndByte o4ib3 = new ObjectWith4IntsAndByte();
        ObjectWith4IntsAndByte o4ib4 = new ObjectWith4IntsAndByte();

        ObjectWith5Ints o5i1 = new ObjectWith5Ints();
        ObjectWith5Ints o5i2 = new ObjectWith5Ints();
        ObjectWith5Ints o5i3 = new ObjectWith5Ints();
        ObjectWith5Ints o5i4 = new ObjectWith5Ints();

        ObjectWithArrayRef oar1 = new ObjectWithArrayRef();
        ObjectWithArrayRef oar2 = new ObjectWithArrayRef();
        ObjectWithArrayRef oar3 = new ObjectWithArrayRef();
        ObjectWithArrayRef oar4 = new ObjectWithArrayRef();

        byte[] a0b1 = new byte[0];
        byte[] a0b2 = new byte[0];
        byte[] a0b3 = new byte[0];
        byte[] a0b4 = new byte[0];

        byte[] a1b1 = new byte[1];
        byte[] a1b2 = new byte[1];
        byte[] a1b3 = new byte[1];
        byte[] a1b4 = new byte[1];

        byte[] a5b1 = new byte[5];
        byte[] a5b2 = new byte[5];
        byte[] a5b3 = new byte[5];
        byte[] a5b4 = new byte[5];

        byte[] a9b1 = new byte[9];
        byte[] a9b2 = new byte[9];
        byte[] a9b3 = new byte[9];
        byte[] a9b4 = new byte[9];

        byte[] a12b1 = new byte[12];
        byte[] a12b2 = new byte[12];
        byte[] a12b3 = new byte[12];
        byte[] a12b4 = new byte[12];

        byte[] a13b1 = new byte[13];
        byte[] a13b2 = new byte[13];
        byte[] a13b3 = new byte[13];
        byte[] a13b4 = new byte[13];

        print("java.lang.Object", o1, o2, o3, o4);
        print("Empty object", eo1, eo2, eo3, eo4);
        print("Object with boolean", ob1, ob2, ob3, ob4);
        print("Object with boolean and int", obi1, obi2, obi3, obi4);
        print("Object with long", ol1, ol2, ol3, ol4);
        print("Object with 4 ints", o4i1, o4i2, o4i3, o4i4);
        print("Object with 4 ints and byte", o4ib1, o4ib2, o4ib3, o4ib4);
        print("Object with 5 ints", o5i1, o5i2, o5i3, o5i4);

        print("Object with array ref", new Object[]{oar1, oar2, oar3, oar4});

        print("new byte[0]", a0b1, a0b2, a0b3, a0b4);
        print("new byte[1]", a1b1, a1b2, a1b3, a1b4);
        print("new byte[5]", a5b1, a5b2, a5b3, a5b4);
        print("new byte[9]", a9b1, a9b2, a9b3, a9b4);
        print("new byte[12]", a12b1, a12b2, a12b3, a12b4);
        print("new byte[13]", a13b1, a13b2, a13b3, a13b4);
    }

    static void print(String title, Object... objects) {
        StringBuilder buf = new StringBuilder(title).append(":");
        int prevHash = objects[0].hashCode();
        int prevDiff = -1;
        for (int i = 1; i < objects.length; i++) {
            int hash = objects[i].hashCode();
            int diff = Math.abs(hash - prevHash);
            if (prevDiff == -1 || prevDiff != diff) {
                buf.append(' ').append(diff);
            }
            prevDiff = diff;
            prevHash = hash;
        }
        System.out.println(buf.toString());
    }

    /******** Test classes ******/

    public static class EmptyObject {
    }

    public static class ObjectWith4Ints {
        int i1;
        int i2;
        int i3;
        int i4;
    }

    public static class ObjectWith4IntsAndByte {
        int i1;
        int i2;
        int i3;
        int i4;
        byte b;
    }

    public static class ObjectWith5Ints {
        int i1;
        int i2;
        int i3;
        int i4;
        int i5;
    }

    public static class ObjectWithArrayRef {
        byte[] b;
    }

    public static class ObjectWithBoolean {
        boolean b;
    }

    public static class ObjectWithBooleanAndInt {
        boolean b;
        int i;
    }

    public static class ObjectWithLong {
        long l;
    }
}

和这里的结果:

java.lang.Object: 16
Empty object: 16
Object with boolean: 16
Object with boolean and int: 24
Object with long: 24
Object with 4 ints: 32
Object with 4 ints and byte: 32
Object with 5 ints: 32
Object with array ref: 16
new byte[0]: 24
new byte[1]: 24
new byte[5]: 32
new byte[9]: 32
new byte[12]: 32
new byte[13]: 40

要总结的结果:

  • 8字节边界对齐是一样的热点,而这也就是同样的唯一的事情。

  • 8 byte boundary alignment is the same as on HotSpot, and that's the only thing that is the same.

最低的16个字节为一个普通的对象(VS 8的HotSpot)

minimum of 16 bytes for a plain Object (vs 8 on HotSpot)

显然是一个空的对象本身占用12个字节(与上热点8)和有房4个额外的字节,直到对象大小跳跃,从16个字节到24个字节的一个边界。

apparently an empty object itself occupies 12 bytes (vs 8 on HotSpot) and there is room for 4 extra bytes until object size 'jumps' from 16 bytes to the next boundary of 24 bytes.

最低的24个字节为一个空数组(VS 12日热点)

minimum of 24 bytes for an empty array (vs 12 on HotSpot)

相似的阵列本身占用20个字节(VS 12的HotSpot),并有空间阵列数据的4个额外的字节,直到对象大小跳跃,从24字节到32字节下一个边界。

similarly an array itself occupies 20 bytes (vs 12 on HotSpot) and there is room for 4 extra bytes of array data until object size 'jumps' from 24 bytes to the next boundary of 32 bytes.

增加:(响应路易的建议) 另一个压力测试表明,即使分配对象实例的百万任何两个之间的距离绝不小于16字节。这就是证明对象之间潜在的8个字节的孔用于进一步的分配肯定是死的空间,否则的时候,大约有一半已经分配给对象的Dalvik内存绝对应该已经把其中的一些进洞为好,而压力测试将返回8,而不是16。

ADDITION: (in response to Louis' suggestion) Another stress test shows that even allocating a million of Object instances the distance between any two is NEVER less than 16 bytes. That's the proof that potential 8-byte holes between the objects are definitely dead space for further allocations, otherwise by the time when about half the memory has been allocated for Objects dalvik should definitely have been putting some of them into 'holes' as well, and the stress test would return 8, not 16.

public static void run2() {
    int count = 1024 * 1024;
    Object[] arr = new Object[count];
    for (int i = 0; i < count; i++) {
        arr[i] = new Object();
    }
    int[] hashes = new int[count];
    for (int i = 0; i < count; i++) {
        hashes[i] = arr[i].hashCode();
    }
    Arrays.sort(hashes);

    int minDist = Integer.MAX_VALUE;
    for (int i = 1; i < count; i++) {
        int dist = Math.abs(hashes[i] - hashes[i - 1]);
        if (dist < minDist) {
            minDist = dist;
        }
    }
    System.out.println("Allocated "+ count + " Objects, minimum distance is "+ minDist);
}

我会看到它的权利是的Dalvik的对象最多需要8个字节阵列8-12多个字节相比,热点?

推荐答案

(是的,这是一个老问题,但结果排序有趣的,所以我在这戳了一下。)

(Yes, this is an old question, but the results were sort of interesting so I poked at it a bit.)

Object.clone()方法需要建立一个对​​象的完整逐位复制。为此,它需要知道有多大的目的是。如果你看看 dvmCloneObject(),您可以看到它使用一个方法数组和不同方法的对象。

The Object.clone() method needs to make a full bitwise copy of an object. To do so, it needs to know how big an object is. If you look at dvmCloneObject(), you see that it uses one method for arrays and a different method for objects.

有关的阵列,它调用 dvmArrayObjectSize(),它乘该阵列的长度由元件宽度(1,2,4,或8),然后加上偏移量从对象的起始处的阵列的数据。每个对象具有一个8字节首标;阵列具有4个字节的宽度,并包括额外的4个字节的填充,以确保64位值正确对齐。因此,对于有5个元素的数组,这将是16 + 5 * 2。

For arrays, it calls dvmArrayObjectSize(), which multiplies the array length by the element width (1, 2, 4, or 8), and then adds the offset of the array data from the start of the object. Every object has an 8-byte header; arrays have a 4-byte width and include an additional 4 bytes of padding to ensure that 64-bit values are aligned properly. So for a 5-element array of short, it would be 16 + 5 * 2.

对于普通的对象,它只是使用了 objectSize 字段中的类对象。这是通过一个叫做相当复杂的功能设置 computeFieldOffsets()。该功能可确保所有的对象引用来第一次(这样的GC可以跳过扫描周围少的时候),然后得出与所有的64位字段。为确保64位字段正确对齐,它可能将32位原始的领域之一多达垫东西出来。 (如果没有相应的32位字段,你只得到4字节的填充。)

For ordinary objects, it just uses the objectSize field in the class object. This is set by a rather complicated function called computeFieldOffsets(). That function ensures that all object references come first (so the GC can skip around less when scanning), then follows that with all of the 64-bit fields. To ensure that the 64-bit fields are properly aligned, it may move one of the 32-bit primitive fields up to pad things out. (If there's no appropriate 32-bit field, you just get 4 bytes of padding.)

我要补充:所有字段都是32位的,除了,这是64位。对象引用是32位。

I should add: all fields are 32-bit, except long and double, which are 64-bit. Object references are 32-bit.

所以它的棘手说究竟有多大的非数组对象会,但一般你把8个字节的对象标题,总结附加字段的宽度,并四舍五入到8个字节的倍数 - 最后,因为所有的对象必须是64位对齐

So it's tricky to say exactly how big a non-array object will be, but in general you take the 8-byte object header, sum up the widths of the additional fields, and round up to the next multiple of 8 bytes -- the last because all objects must be 64-bit aligned.

这就是理论。要看到它在实践中,我已将此添加 dvmCloneObject()

So that's the theory. To see it in practice, I added this to dvmCloneObject():

ALOGD("class=%s size=%d", clazz->descriptor, clazz->objectSize);

和见过这样的logcat输出:

and saw logcat output like:

D dalvikvm: class=Ljava/util/Locale; size=24
D dalvikvm: class=Ljava/util/Date; size=16

区域设置有4个参考字段,日期已经有一个字段,所以这些值匹配的期望。

Locale has 4 reference fields, Date has one long field, so these values match expectations.

在理想情况下,这到底有多少空间将需要。但是,对象被分配与 mspace_calloc(),这又增加了4个或(有时)8个字节的开销。因此,所需值实际的上方空间将是32和24,这符合你的实验结果。

Ideally, that's exactly how much space would be required. However, the object is allocated with mspace_calloc(), which adds another 4 or (sometimes) 8 bytes of overhead. So the actual space required for the values above would be 32 and 24, which matches your experimental results.

这篇关于是的Dalvik甚至更多的内存中对象的尺寸方面比饿热点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆