Как лучше всего определить размер объекта в Java?

StackOverflow https://stackoverflow.com/questions/52353

  •  09-06-2019
  •  | 
  •  

Вопрос

Например, предположим, что у меня есть приложение, которое может читать CSV-файл с множеством строк данных.Я предоставляю пользователю сводную информацию о количестве строк в зависимости от типов данных, но хочу быть уверен, что не читаю слишком много строк данных и не вызываю OutOfMemoryErrorс.Каждая строка преобразуется в объект.Есть ли простой способ узнать размер этого объекта программно?Существует ли ссылка, определяющая, насколько велики примитивные типы и ссылки на объекты для VM?

Прямо сейчас у меня есть код, который говорит, что прочтите до 32 000 строк, но я также хотел бы иметь код, который говорит, что нужно читать как можно больше строк, пока я не воспользуюсь 32 МБ памяти.Возможно, это другой вопрос, но мне все равно хотелось бы знать.

Это было полезно?

Решение

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

Скомпилируйте и поместите этот класс в JAR:

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);
    }
}

Добавьте следующее в свой MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Используйте getObjectSize:

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

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

Вызов с помощью:

java -javaagent:ObjectSizeFetcherAgent.jar C

Другие советы

Вы должны использовать Джол, инструмент, разработанный в рамках проекта OpenJDK.

JOL (Java Object Layout) — это небольшой набор инструментов для анализа схем размещения объектов в JVM.Эти инструменты активно используют Unsafe, JVMTI и Serviceability Agent (SA) для декодирования фактического макета объекта, его контура и ссылок.Это делает JOL гораздо более точным, чем другие инструменты, использующие дампы кучи, предположения спецификаций и т. д.

Чтобы получить размеры примитивов, ссылок и элементов массива, используйте VMSupport.vmDetails().В Oracle JDK 1.8.0_40, работающем в 64-разрядной версии Windows (используется во всех следующих примерах), этот метод возвращает

Running 64-bit HotSpot VM.
Using compressed oop with 0-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

Вы можете получить небольшой размер экземпляра объекта, используя ClassLayout.parseClass(Foo.class).toPrintable() (необязательно передача экземпляра в toPrintable).Это только пространство, занимаемое одним экземпляром этого класса;он не включает в себя какие-либо другие объекты, на которые ссылается этот класс.Это делает включите служебные данные VM для заголовка объекта, выравнивания полей и заполнения.Для java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

Вы можете получить сводное представление о глубоком размере экземпляра объекта, используя GraphLayout.parseInstance(obj).toFootprint().Конечно, некоторые объекты в посадочном месте могут быть общими (на которые также ссылаются другие объекты), поэтому это чрезмерное приближение к пространству, которое может быть освобождено при сборке мусора для этого объекта.Для результата Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (взято из этот ответ), jol сообщает об общем объеме 1840 байт, из которых только 72 — это сам экземпляр Pattern.

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

Если вместо этого вы используете GraphLayout.parseInstance(obj).toPrintable(), jol сообщит вам адрес, размер, тип, значение и путь разыменования полей для каждого объекта, на который ссылаются, хотя обычно это слишком много деталей, чтобы быть полезными.Для текущего примера шаблона вы можете получить следующее.(Адреса, скорее всего, будут меняться между запусками.)

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

Записи «(что-то еще)» описать другие объекты в куче, которые не являются частью этого графа объектов.

Лучшая документация jol — это веселые образцы в репозитории jol.В примерах демонстрируются распространенные операции jol и показано, как можно использовать jol для анализа внутренних компонентов виртуальной машины и сборщика мусора.

Несколько лет назад в Javaworld была статья об определении размера составных и потенциально вложенных объектов Java, они в основном создают реализацию sizeof() на Java.Этот подход в основном основан на других работах, в которых люди экспериментально определяли размер примитивов и типичных объектов Java, а затем применяли эти знания к методу, который рекурсивно обходит граф объектов для подсчета общего размера.

Она всегда будет несколько менее точной, чем собственная реализация C, просто из-за того, что происходит за кулисами класса, но это должно быть хорошим индикатором.

В качестве альтернативы проект SourceForge с соответствующим названием размер который предлагает библиотеку Java5 с реализацией sizeof().

P.S.Не используйте подход сериализации: нет корреляции между размером сериализованного объекта и объемом памяти, которую он потребляет при работе.

Во-первых, «размер объекта» не является четко определенным понятием в Java.Вы можете иметь в виду сам объект только с его членами, объектом и всеми объектами, на которые он ссылается (ссылочный граф).Вы можете иметь в виду размер в памяти или размер на диске.И JVM разрешено оптимизировать такие вещи, как строки.

Поэтому единственный правильный способ — обратиться к JVM с помощью хорошего профилировщика (я использую ВашКит), что, вероятно, не то, что вам нужно.

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

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

Помните, что если у вас есть объекты с общими ссылками, это не будет дают правильный результат, и размер сериализации не всегда будет соответствовать размеру в памяти, но это хорошее приближение.Код будет немного более эффективным, если вы инициализируете размер ByteArrayOutputStream разумным значением.

Я случайно нашел класс Java "jdk.nashorn.internal.ir.debug.objectsizecalculator", уже в JDK, который прост в использовании и кажется весьма полезным для определения размера объекта.

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

Результаты:

164192
48
16
48
416

Если вы просто хотите узнать, сколько памяти используется в вашей JVM и сколько свободно, вы можете попробовать что-то вроде этого:

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

редактировать:Я подумал, что это может быть полезно, поскольку автор вопроса также заявил, что хотел бы иметь логику, которая бы обрабатывала «чтение как можно большего количества строк, пока я не использую 32 МБ памяти».

Когда я работал в Твиттере, я написал утилиту для расчета размера глубоких объектов.Он учитывает различные модели памяти (32-битная, сжатая, ой, 64-битная), заполнение, заполнение подклассов, корректно работает с циклическими структурами данных и массивами.Вы можете просто скомпилировать этот файл .java;он не имеет внешних зависимостей:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java

Многие другие ответы дают мелкие размеры - например.размер HashMap без каких-либо ключей или значений, что вряд ли вам нужно.

Проект jamm использует пакет java.lang.instrumentation, описанный выше, но обходит дерево и поэтому может обеспечить более глубокое использование памяти.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Чтобы использовать MemoryMeter, запустите JVM с помощью «-javaagent:/jamm.jar».

Вам придется ходить по объектам, используя отражение.Будьте осторожны:

  • Простое выделение объекта требует некоторых накладных расходов в JVM.Сумма зависит от JVM, поэтому вы можете сделать это значение параметром.По крайней мере, сделайте его константой (8 байт?) и примените ко всему выделенному.
  • Да просто так byte теоретически составляет 1 байт, это не означает, что он занимает только один байт в памяти.
  • В ссылках на объекты будут циклы, поэтому вам нужно будет сохранить HashMap или что-то в этом роде использование object-equals в качестве компаратора для устранения бесконечных циклов.

@jodonnell:Мне нравится простота вашего решения, но многие объекты не являются сериализуемыми (поэтому это приведет к возникновению исключения), поля могут быть временными, а объекты могут переопределять стандартные методы.

Вам придется измерить его с помощью инструмента или оценить вручную, и это зависит от используемой вами JVM.

Существуют некоторые фиксированные накладные расходы на каждый объект.Это зависит от JVM, но я обычно оцениваю 40 байт.Затем вам нужно посмотреть на членов класса.Ссылки на объекты занимают 4 (8) байтов в 32-битной (64-битной) JVM.Примитивные типы:

  • логическое значение и байт:1 байт
  • символ и краткое:2 байта
  • int и float:4 байта
  • длинный и двойной:8 байт

Массивы подчиняются тем же правилам;то есть это ссылка на объект, которая занимает 4 (или 8) байт в вашем объекте, а затем его длина, умноженная на размер его элемента.

Попытка сделать это программно с помощью вызовов Runtime.freeMemory() просто не дает особой точности из-за асинхронных вызовов сборщика мусора и т. д.Профилирование кучи с помощью -Xrunhprof или других инструментов даст наиболее точные результаты.

А 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() { }
}

Существует также Измеритель памяти инструмент (ранее в Google-код, сейчас на GitHub), который прост и опубликован под коммерческой лицензией. Лицензия Апач 2.0, как обсуждалось в аналогичный вопрос.

Он также требует аргумента командной строки для интерпретатора Java, если вы хотите измерить потребление байтов памяти, но в остальном, похоже, работает нормально, по крайней мере, в тех сценариях, которые я использовал.

Вот утилита, которую я создал, используя некоторые из связанных примеров, для обработки 32-битных, 64-битных и 64-битных версий со сжатым ООП.Оно использует sun.misc.Unsafe.

Оно использует Unsafe.addressSize() чтобы получить размер собственного указателя и Unsafe.arrayIndexScale( Object[].class ) для размера ссылки Java.

Он использует смещение поля известного класса для определения базового размера объекта.

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}

Без необходимости возиться с инструментами и т. д., и если вам не нужно знать размер объекта с точностью до байта, вы можете использовать следующий подход:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Таким образом, вы считываете использованную память до и после, а вызов GC непосредственно перед получением использованной памяти снижает «шум» почти до 0.

Для более надежного результата вы можете запустить задание n раз, а затем разделить используемую память на n и получить, сколько памяти занимает один запуск.Более того, вы можете запустить все это еще раз и получить среднее значение.

Вызова метода нет, если вы об этом просите.Полагаю, после небольшого исследования вы могли бы написать свою собственную.Конкретный экземпляр имеет фиксированный размер, определяемый количеством ссылок и примитивных значений, а также данными бухгалтерского учета экземпляра.Вы просто пройдёте по графу объекта.Чем менее разнообразны типы строк, тем проще.

Если это слишком медленно или просто больше проблем, чем пользы, всегда есть старое доброе эмпирическое правило подсчета строк.

Однажды я написал быстрый тест, чтобы оценить на лету:

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

Общая концепция заключается в распределении объектов и измерении изменений в свободном пространстве кучи.Ключевым моментом является getFreeMemory(), который запрашивает запуск GC и ждет, пока сообщенный размер свободной кучи стабилизируется.Результат вышеизложенного:

nested:        160000 each=16
static nested: 160000 each=16

Именно этого мы и ожидаем, учитывая поведение выравнивания и возможные накладные расходы на заголовок блока кучи.

Метод инструментирования, подробно описанный в принятом здесь ответе, является наиболее точным.Описанный мной метод является точным, но только в контролируемых условиях, когда никакие другие потоки не создают/удаляют объекты.

Просто используйте Java Visual VM.

В нем есть все необходимое для профилирования и устранения проблем с памятью.

Он также имеет консоль OQL (язык объектных запросов), которая позволяет вам делать много полезных вещей, одна из которых — sizeof(o)

Мой ответ основан на коде, предоставленном Ником.Этот код измеряет общее количество байтов, занимаемых сериализованным объектом.Таким образом, это на самом деле измеряет материал сериализации + объем памяти простого объекта (просто сериализуйте, например int и вы увидите, что общее количество сериализованных байт не равно 4).Поэтому, если вы хотите, чтобы необработанный номер байта использовался именно для вашего объекта, вам нужно немного изменить этот код.Вот так:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

Я протестировал это решение с примитивными типами, String и некоторыми тривиальными классами.Могут быть и не охваченные случаи.


ОБНОВЛЯТЬ: Пример изменен для поддержки расчета объема памяти объектов массива.

Вы можете создать дамп кучи (например, с помощью jmap), а затем проанализировать выходные данные, чтобы определить размеры объектов.Это автономное решение, но вы можете исследовать мелкие и глубокие размеры и т. д.

long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

Размер дает вам увеличение использования памяти jvm из-за создания объекта, и обычно это размер объекта.

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

Таким образом, массивы, списки или карты всех этих коллекций на самом деле не будут хранить объекты (только во время примитивов необходим реальный размер памяти объекта), они будут хранить только ссылки на эти объекты.

Сейчас Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 байт) зависит от (32/64 бит) ОС

ПРИМИТИВЫ

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

ОБЪЕКТЫ

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

Я имею в виду, что для всего объекта REFERENCE требуется всего 4 байта памяти.Это может быть ссылка на строку ИЛИ ссылка на двойной объект. Но в зависимости от создания объекта требуемая память будет различаться.

например) Если я создаю объект для приведенного ниже класса ReferenceMemoryTest тогда будет создано 4 + 4 + 4 = 12 байт памяти.Память может отличаться при попытке инициализировать ссылки.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

Поэтому при создании массива объектов/ссылок все его содержимое будет занято NULL-ссылками.И мы знаем, что каждая ссылка требует 4 байта.

И, наконец, выделение памяти для приведенного ниже кода составляет 20 байт.

ReferenceMemoryTest ref1 = новый ReferenceMemoryTest();(4 (Ref1) + 12 = 16 байт) Ссылка( 4(ref2) + 16 = 20 байт)

Предположим, я объявляю класс с именем Complex нравиться:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

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

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex

Для JSONObject вам может помочь приведенный ниже код.

`JSONObject.toString().getBytes("UTF-8").length`

возвращает размер в байтах

Я проверил это с помощью своего объекта JSONArray, записав его в файл.Это дает размер объекта.

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

Один из способов сделать это — сериализовать объект в файл и посмотреть размер файла, например:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

Конечно, это предполагает, что каждый объект индивидуален и не содержит постоянных ссылок ни на что другое.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top