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

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

    Хотя примитивы должны вести себя так, как если бы они имели конкретные размеры, JVM может хранить данные любым удобным способом, с любым размером и накладными расходами. Она может хранить boolean[ ] в 64-битных длинных кусках, таких как BitSet, сохранять временные объекты в стек или оптимизировать некоторые переменные или вызовы методов, заменяя их константами и т.д. Все эти оптимизации делаются при условии, что программа будет выдавать тот же результат, что и раньше и это прекрасно

    Учитывая влияние аппаратного обеспечения и операционной системы на наши программы (данные которых могут дублироваться на каждом уровне кэша), мы можем только грубо прогнозировать потребление оперативной памяти
      2.1 Объекты, ссылки и классы-обертки
      Минимальный размер объекта для современного 64-битного JDK составляет 16 байт, так как объект имеет 12-байтовый заголовок, дополненный до размера, кратного 8-ми байтам. В 32-битной JDK, накладные расходы составляют 8 байт, дополненные до кратности 4-ем

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

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

      Особенно важно отметить, что классы-обертки, массивы, строки, многомерные массивы являются дорогостоящими, поскольку они добавляют определенные расходы. Например, если мы сравним примитивный тип int (занимает 4 байта) с его оберткой Integer (занимает 16 байт), то накладные расходы составят 300%
        3. Оценка размера объекта с помощью Java Instrumentation
        Одним из способов получить размер объекта в Java является использование метода getObjectSize(Object) интерфейса Instrumentation, введенного в Java 5

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

        Этот подход позволяет произвести только оценку рассматриваемого объекта, но не размеры объектов на которые он ссылается. Чтобы рассчитать полный размер объекта, нам необходим код, который пробежится по всем ссылкам и рассчитает примерный размер
          3.1 Создание Instrumentation Agent
          Чтобы вызвать Instrumentation.getObjectSize(Object), для получения размера объекта, мы должны иметь возможность сначала получить доступ к экземпляру Instrumentation. Для этого нам нужно использовать Instrumentation Agent, и есть два способа сделать это, как описано в документации для пакета java.lang.instrument:

          1. Instrumentation Agent может быть указан через командную строку
          2. мы можем использовать его с уже запущенной JVM

          Давайте сосредоточимся на первом способе

          Чтобы указать Instrumentation Agent через командную строку, нам понадобится реализация перегруженного метода 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-объектов