Есть ли простой способ получить размер java-объекта?

Я хотел бы знать любой простой способ получить размер java-объекта? Кроме того, в любом случае, чтобы получить размер класса, такого как operator sizeof в С++?

Ответ 2

Интерфейс Instrumentation имеет метод getObjectSize().

Однако это дает только размер самого объекта, а не его субобъектов. Так, например, он скажет вам, что все объекты String имеют одинаковый размер.

Другая проблема заключается в том, что размер объекта может фактически изменяться спонтанно. Например, если вы получите идентификатор хэш-кода объекта и он переживает цикл GC, тогда его размер будет увеличен (по крайней мере) на 4 байта, чтобы сохранить значение идентификатора хэш-кода.


Проблема нахождения размера "объекта" заключается в том, что невозможно, чтобы общий класс/метод полезности точно знал, где границы абстракции произвольного объекта. Есть проблемы для чего-то даже такого простого, как класс String. (Рассмотрим объекты String, созданные с помощью substring(...) в Java 6. Вы могли бы объект char[] value как часть this, или часть исходной строки, или и то, и другое? Что это означает для размеров соответствующих объектов?)

Таким образом, неизбежно, что что-то вроде net.sourceforge.sizeof не может дать абсолютно точный учет использования пространства.

Ответ 3

Используя класс Unsafe из sun.misc, вы можете получить смещение полей. Итак, учитывая выравнивание кучи объектов в соответствии с архитектурой процессора и вычисление максимального смещения поля, вы можете измерить размер объекта Java. В приведенном ниже примере я использую вспомогательный класс UtilUnsafe для получения ссылки на объект sun.misc.Unsafe.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}

Ответ 4

Пока ваша программа запущена, запустите другое окно терминала и запустите:

jmap -histo <process id of the java instance you want to debug>

В выводе указывается количество и общий размер экземпляров объектов по классам. Например, 3 72 java.util.Date означает, что в памяти записано 3 объекта Date по 24 байта каждый. Вам может потребоваться передать вывод в файл или что-то, если соответствующая часть прокручивается слишком быстро.

Или, для (большей) детали, запустите:

jmap -dump:format=b,file=heap.bin <processid>
jhat heap.bin

Затем откройте http://localhost:7000. Вы можете просматривать объекты в куче в своем браузере.

Дополнительная информация:

http://docs.oracle.com/javase/6/docs/technotes/tools/share/jmap.html
http://docs.oracle.com/javase/6/docs/technotes/tools/share/jhat.html

Я думаю, что кстати, он всегда округляется до 8, в Sun/Oracle JVM, даже если объект имеет 12 байтов, он занимает 16 в памяти.

Ответ 5

Определенно можно получить размер объекта, потому что объект определенного класса является блоком памяти размером с фиксированный размер. Я написал следующий код для вычисления сохраненного размера объекта. Он также предоставляет такой метод, как "sizeof" в С++. Он готов к запуску и ни на что не зависит. Вы можете скопировать и попробовать!

public class ObjectSizer {

    public static final Unsafe us = getUnsafe();

    public static boolean useCompressedOops = true;

    public static int retainedSize(Object obj) {
        return retainedSize(obj, new HashMap<Object, Object>());
    }

    private static int retainedSize(Object obj, HashMap<Object, Object> calculated) {
        try {
            if (obj == null)
                throw new NullPointerException();
            calculated.put(obj, obj);
            Class<?> cls = obj.getClass();
            if (cls.isArray()) {
                int arraysize = us.arrayBaseOffset(cls) + us.arrayIndexScale(cls) * Array.getLength(obj);
                if (!cls.getComponentType().isPrimitive()) {
                    Object[] arr = (Object[]) obj;
                    for (Object comp : arr) {
                        if (comp != null && !isCalculated(calculated, comp))
                            arraysize += retainedSize(comp, calculated);
                    }
                }
                return arraysize;
            } else {
                int objectsize = sizeof(cls);
                for (Field f : getAllNonStaticFields(obj.getClass())) {
                    Class<?> fcls = f.getType();
                    if (fcls.isPrimitive())
                        continue;
                    f.setAccessible(true);
                    Object ref = f.get(obj);
                    if (ref != null && !isCalculated(calculated, ref)) {
                        int referentSize = retainedSize(ref, calculated);
                        objectsize += referentSize;
                    }
                }
                return objectsize;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static int sizeof(Class<?> cls) {

        if (cls == null)
            throw new NullPointerException();

        if (cls.isArray())
            throw new IllegalArgumentException();

        if (cls.isPrimitive())
            return primsize(cls);

        int lastOffset = Integer.MIN_VALUE;
        Class<?> lastClass = null;

        for (Field f : getAllNonStaticFields(cls)) {
            if (Modifier.isStatic(f.getModifiers()))
                continue;

            int offset = (int) us.objectFieldOffset(f);
            if (offset > lastOffset) {
                lastOffset = offset;
                lastClass = f.getClass();
            }
        }
        if (lastOffset > 0)
            return modulo8(lastOffset + primsize(lastClass));
        else
            return 16;
    }

    private static Field[] getAllNonStaticFields(Class<?> cls) {
        if (cls == null)
            throw new NullPointerException();

        List<Field> fieldList = new ArrayList<Field>();
        while (cls != Object.class) {
            for (Field f : cls.getDeclaredFields()) {
                if (!Modifier.isStatic(f.getModifiers()))
                    fieldList.add(f);
            }
            cls = cls.getSuperclass();
        }
        Field[] fs = new Field[fieldList.size()];
        fieldList.toArray(fs);
        return fs;
    }

    private static boolean isCalculated(HashMap<Object, Object> calculated, Object test) {
        Object that = calculated.get(test);
        return that != null && that == test;
    }

    private static int primsize(Class<?> cls) {
        if (cls == byte.class)
            return 1;
        if (cls == boolean.class)
            return 1;
        if (cls == char.class)
            return 2;
        if (cls == short.class)
            return 2;
        if (cls == int.class)
            return 4;
        if (cls == float.class)
            return 4;
        if (cls == long.class)
            return 8;
        if (cls == double.class)
            return 8;
        else
            return useCompressedOops ? 4 : 8;
    }

    private static int modulo8(int value) {
        return (value & 0x7) > 0 ? (value & ~0x7) + 8 : value;
    }

    private static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            return (Unsafe) f.get(Unsafe.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        System.out.println(retainedSize("Hello Leeeeeeeen"));
    }
}