Вычислить размер объекта в Java

Я хочу записать, сколько памяти (в байтах, надеюсь) объект принимает для проекта (я сравниваю размеры структур данных), и кажется, что в Java нет способа сделать это. Предположительно, у C/С++ есть метод sizeOf(), но это неэксистантно в Java. Я пробовал записывать свободную память в JVM с помощью Runtime.getRuntime().freeMemory() до и после создания объекта, а затем записывать разницу, но он будет давать только 0 или 131304 и ничего между ними, независимо от количества элементов в структуре. Помогите пожалуйста!

Ответ 1

Вы можете использовать пакет java.lang.instrumentation:

http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html

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

Ответ, связанный с Сергеем, имеет отличный пример, который я буду перепечатывать здесь, но вы должны были уже посмотреть на его комментарий:

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

Использовать getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

источник, например:

В Java, какой лучший способ определить размер объекта?

Ответ 2

Посмотрите https://github.com/DimitrisAndreou/memory-measurer Guava использует его внутри, а ObjectGraphMeasurer особенно просто использовать готовые, без каких-либо специальных аргументы командной строки.  

import objectexplorer.ObjectGraphMeasurer;

public class Measurer {

  public static void main(String[] args) {
    Set<Integer> hashset = new HashSet<Integer>();
    Random random = new Random();
    int n = 10000;
    for (int i = 1; i <= n; i++) {
      hashset.add(random.nextInt());
    }
    System.out.println(ObjectGraphMeasurer.measure(hashset));
  }
}

Ответ 3

Класс java.lang.instrument.Instrumentation предоставляет хороший способ получить размер Java-объекта, но для этого требуется определить premain и запустить вашу программу с помощью java-агента. Это очень скучно, когда вам не нужен какой-либо агент, а затем вы должны предоставить фиктивный агент Jar для своего приложения.

Итак, я получил альтернативное решение, используя класс 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

Поиск google в "Java sizeof" привел к хорошей статье о Java sizeof на javaworld. На самом деле интересно читать.

Чтобы ответить на ваш вопрос, нет Java-эквивалента "sizeof()", но в статье описывается несколько способов, которыми вы можете получить размер байта ваших созданных экземпляров.