Как получить размер объекта в Java

1. Обзор

В отличие от C/C, где мы можем использовать метод sizeof _ () _ для получения размера объекта в байтах, в Java нет реального эквивалента такого метода.

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

2. Потребление памяти в Java

Хотя в Java нет оператора sizeof , на самом деле он нам не нужен. Все примитивные типы имеют стандартный размер, и, как правило, нет байтов заполнения или выравнивания. Тем не менее, это не всегда просто.

  • Хотя примитивы должны вести себя так, как если бы они имели официальные размеры, JVM может хранить данные любым удобным для себя способом, с любым количеством дополнений или накладных расходов ** . Он может хранить boolean[] ​​в 64-битных длинных чанках, таких как BitSet , выделять некоторые временные _Object _ в стеке или полностью оптимизировать некоторые переменные или вызовы методов, заменяя их константами и т. Д. Но, если долго так как программа дает тот же результат, это прекрасно.

Принимая во внимание также влияние аппаратного кэша и кэша ОС (наши данные могут дублироваться на каждом уровне кэша), это означает, что мы можем только приблизительно прогнозировать потребление ОЗУ .

2.1. Объекты, ссылки и классы обертки

  • Минимальный размер объекта составляет 16 байт для современного 64-разрядного JDK ** , поскольку объект имеет 12-байтовый заголовок, дополненный кратным 8 байтам. В 32-битном JDK служебная информация составляет 8 байтов, дополненная кратным 4 байта.

  • Ссылки имеют типичный размер 4 байта на 32-битных платформах и на 64-битных платформах ** с границей кучи менее 32 ГБ ( -Xmx32G ) и 8 байтами для этой границы выше 32 ГБ.

Это означает, что 64-битной JVM обычно требуется на 30-50% больше места в куче.

Особенно важно отметить, что упакованные типы, массивы, _String s и другие контейнеры, такие как многомерные массивы, требуют больших затрат памяти, поскольку они добавляют определенные накладные расходы . Например, когда мы сравниваем примитив int (который занимает всего 4 байта) с объектом Integer_, который занимает 16 байтов, мы видим, что объем памяти составляет 300%.

3. Оценка размера объекта с помощью инструментов

Один из способов получить оценку размера объекта в Java - использовать метод getObjectSize (Object) для Instrumentation interface введен в Java 5.

Как мы могли видеть в документации Javadoc, метод обеспечивает «аппроксимацию, зависящую от реализации» размера указанного объекта.

Следует отметить, что существует потенциальное включение в размер служебных данных, и значения могут отличаться во время одного вызова JVM.

  • Этот подход поддерживает только оценку размера самого рассматриваемого объекта, а не размеры объектов, на которые он ссылается ** . Чтобы оценить общий размер объекта, нам нужен код, который будет проходить по этим ссылкам и вычислять приблизительный размер.

3.1. Создание Инструментовщика

Чтобы вызвать Instrumentation.getObjectSize (Object) , чтобы получить размер объекта, нам нужно сначала получить доступ к экземпляру Instrumentation. Нам нужно использовать инструментальный агент , и есть два способа сделать это, как описано в документации для https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package -summary.html[ java.lang.instrument package.]

  • Инструментальный агент может быть указан через командную строку или мы можем использовать его с уже работающей JVM ** . Мы сосредоточимся на первом.

Чтобы указать агент инструментария через командную строку , нам потребуется реализация перегруженного метода premain , который будет сначала вызываться JVM при использовании инструментария. Кроме того, нам нужно предоставить статический метод, чтобы иметь доступ к Instrumentation.getObjectSize (Object) .

Давайте теперь создадим класс InstrumentationAgent :

public class InstrumentationAgent {
    private static volatile Instrumentation globalInstrumentation;

    public static void premain(final String agentArgs, final Instrumentation inst) {
        globalInstrumentation = inst;
    }

    public static long getObjectSize(final Object object) {
        if (globalInstrumentation == null) {
            throw new IllegalStateException("Agent not initialized.");
        }
        return globalInstrumentation.getObjectSize(object);
    }
}
  • Прежде чем мы создадим JAR для этого агента, мы должны убедиться, что в него включен простой метафайл MANIFEST.MF ** :

Premain-class: com.baeldung.objectsize.InstrumentationAgent

Теперь мы можем создать агент JAR с включенным файлом MANIFEST.MF. Один из способов через командную строку:

javac InstrumentationAgent.java
jar cmf MANIFEST.MF InstrumentationAgent.jar InstrumentationAgent.class

3.2. Пример класса

Давайте посмотрим на это в действии, создав класс с примерами объектов, которые будут использовать наш класс агента:

public class InstrumentationExample {

    public static void printObjectSize(Object object) {
        System.out.println("Object type: " + object.getClass() +
          ", size: " + InstrumentationAgent.getObjectSize(object) + " bytes");
    }

    public static void main(String[]arguments) {
        String emptyString = "";
        String string = "Estimating Object Size Using Instrumentation";
        String[]stringArray = { emptyString, string, "com.baeldung" };
        String[]anotherStringArray = new String[100];
        List<String> stringList = new ArrayList<>();
        StringBuilder stringBuilder = new StringBuilder(100);
        int maxIntPrimitive = Integer.MAX__VALUE;
        int minIntPrimitive = Integer.MIN__VALUE;
        Integer maxInteger = Integer.MAX__VALUE;
        Integer minInteger = Integer.MIN__VALUE;
        long zeroLong = 0L;
        double zeroDouble = 0.0;
        boolean falseBoolean = false;
        Object object = new Object();

        class EmptyClass {
        }
        EmptyClass emptyClass = new EmptyClass();

        class StringClass {
            public String s;
        }
        StringClass stringClass = new StringClass();

        printObjectSize(emptyString);
        printObjectSize(string);
        printObjectSize(stringArray);
        printObjectSize(anotherStringArray);
        printObjectSize(stringList);
        printObjectSize(stringBuilder);
        printObjectSize(maxIntPrimitive);
        printObjectSize(minIntPrimitive);
        printObjectSize(maxInteger);
        printObjectSize(minInteger);
        printObjectSize(zeroLong);
        printObjectSize(zeroDouble);
        printObjectSize(falseBoolean);
        printObjectSize(Day.TUESDAY);
        printObjectSize(object);
        printObjectSize(emptyClass);
        printObjectSize(stringClass);
    }

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
}

Чтобы это работало, нам нужно включить опцию – javaagent с путем к агенту JAR при запуске нашего приложения :

VM Options: -javaagent:"path__to__agent__directory\InstrumentationAgent.jar"

Результат запуска нашего класса покажет нам приблизительные размеры объектов:

Object type: class java.lang.String, size: 24 bytes
Object type: class java.lang.String, size: 24 bytes
Object type: class[Ljava.lang.String;, size: 32 bytes
Object type: class[Ljava.lang.String;, size: 416 bytes
Object type: class java.util.ArrayList, size: 24 bytes
Object type: class java.lang.StringBuilder, size: 24 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Long, size: 24 bytes
Object type: class java.lang.Double, size: 24 bytes
Object type: class java.lang.Boolean, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$Day, size: 24 bytes
Object type: class java.lang.Object, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$1EmptyClass, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$1StringClass, size: 16 bytes

4. Заключение

В этой статье мы описали, как память используется определенными типами в Java, как JVM хранит данные и подчеркнули вещи, которые могут повлиять на общее потребление памяти. Затем мы продемонстрировали, как мы можем на практике получить приблизительный размер объектов Java.

Как всегда, полный код, связанный с этой статьей, можно найти в проекте GitHub .