Рубрики
Без рубрики

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

Краткое и практическое руководство по получению размера объекта в Java.

Автор оригинала: baeldung.

1. Обзор

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

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

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

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

Хотя примитивы должны вести себя так, как если бы они имели официальные размеры, JVM может хранить данные любым удобным для себя способом, с любым количеством заполнений или накладных расходов . Он может хранить логическое[] в 64-разрядных длинных блоках , таких как Набор бит , выделять некоторые временные Объекты в стеке или оптимизировать некоторые переменные или вызовы методов, полностью исключив их из существования, заменяя их константами и т. Д… Но до тех пор, пока программа дает тот же результат, все в полном порядке.

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

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

Минимальный размер объекта составляет 16 байт для современного 64-разрядного JDK , так как объект имеет 12-байтовый заголовок, дополненный кратным 8 байтам. В 32-разрядном JDK накладные расходы составляют 8 байт, что кратно 4 байтам.

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

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

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

3. Оценка Размера Объекта С Помощью Контрольно-Измерительных Приборов

Один из способов получить оценку размера объекта в Java-это использовать getObjectSize(объект) метод Инструментария интерфейса , представленный в Java 5.

Как мы могли видеть в документации Javadoc, метод обеспечивает “аппроксимацию для конкретной реализации” размера указанного объекта. Примечательно, что существует потенциальное включение накладных расходов в размер, и значения могут отличаться во время одного вызова JVM.

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

3.1. Создание Агента Инструментирования

Для того, чтобы позвонить Instrumentation.getObjectSize(объект) чтобы получить размер объекта, нам сначала нужно получить доступ к экземпляру Instrumentation. Нам нужно использовать агент инструментирования и есть два способа сделать это, как описано в документации для пакета java.lang.instrument .

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

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

Давайте теперь создадим класс Агент инструментирования :

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

Теперь мы можем создать банку агента с включенным файлом 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 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 .