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


617

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

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


Я добавил своего агента с помощью конфигов mvn и объяснил, как здесь: stackoverflow.com/a/36102269/711855
июня,

Ответы:


460

Вы можете использовать пакет 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

2
@ Стефан Хороший намек! Можете ли вы сказать, какой будет размер byte[0], byte[1], byte[5], int[0], int[1], int[2]используя подход , вы описали? Было бы хорошо, если бы результаты включали накладные расходы на длину массива и выравнивание памяти.
dma_k

8
Я попробовал это и получил странные и бесполезные результаты. Строки были всегда 32, независимо от размера. Я подумал, что это, возможно, размер указателя, но для другого неизменного класса, который я создал, я получил 24. Он хорошо работает для примитивов, но тогда вам не нужна программа, чтобы сказать вам, насколько большой символ.
Brel

6
@Brel это решение является только «приблизительным объемом памяти, потребляемой указанным объектом», как указано в документации. Также я предполагаю, что авторы решили установить размер строки в 32 байта (только указатель?) Из-за пула строк Java, что затрудняет определение того, является ли экземпляр String общим (хранится в пуле) или местный и уникальный для класса.
Андрей I

11
Как я могу использовать ObjectSizeFetcher, если не экспортирую jar? У меня есть тестовый проект Java в затмении.
Юра Шинкарев

3
@brel Причина, по которой длина строки составляет всего 32 байта независимо от фактической длины, заключается в том, что часть строки переменной длины хранится в char [], который является ее собственным объектом. Чтобы получить истинный размер объекта, вам нужно добавить размер самого себя и размер каждого объекта, на который он ссылается.
tombrown52

117

Вам следует использовать jol , инструмент, разработанный как часть проекта 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(). Конечно, некоторые объекты в элементе footprint могут быть общими (также на них ссылаются другие объекты), так что это избыточное приближение пространства, которое можно восстановить, когда этот объект собирается мусором. Для результата 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 и показано, как можно использовать jol для анализа виртуальных машин и внутренних компонентов сборщика мусора.


18
Этот ответ должен иметь больше голосов. Определенно очень хороший вариант для проверки. РЕДАКТИРОВАТЬ: Проверено, что это было добавлено в этом году, в то время как вопрос был задан в '08. Вероятно, лучший и самый простой вариант сделать то, что ОП спросил в данный момент.
арендует

4
Автор инструмента написал пост в блоге о Джол .
Майк

2
Чтобы определить размер объекта "obj" используйте: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
энергия

Обратите внимание, что vmDetailsсейчас VM.current().details().
Miha_x64

Посмотрите, GraphLayout.parseInstance(instance).toFootprint()я нашел более полезным для понимания размеров объектов
Мюген

82

Я случайно нашел 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

3
Здесь я попробовал другие решения, предложенные выше, и наткнулся на ObjectSizeCalculator. Я считаю, что никто не упомянул, если раньше, так как он был недавно представлен на JDK 8 в рамках проекта Nashorn . Однако я не нашел официальной документации об этом классе в Интернете.
Энрике Гонтихо,

Кажется, он не учитывает длины строк. Это просто размер в стеке?
jontejj

1
У меня есть хэш-карта, где com.carrotsearch.RamUsageEstimator возвращает около половины ObjectSizeCalculator. Какой из них является правдой? - Какой из них более надежный?
Бадера

9
Обратите внимание, что ObjectSizeCalculatorподдерживается только на HotSpot VM
kellanburket

74

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

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

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

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


6
Утилита sizeof, вероятно, самая быстрая. Это в основном то, что сказал Стефан, но уже упакованный в банку, готовую к употреблению.
Александр Л Теллес

62

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

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

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

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

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


2
Мне нравится этот подход. Насколько далеко вы были с точки зрения размера объекта.
Берлин Браун

1
Очень просто и эффективно. Другие методы слишком беспорядочные (особенно внутри Eclipse RCP). Спасибо.
marcolopes

19
Сериализация не будет отслеживать переходные переменные, и метод сериализации по умолчанию записывает строки в UTF-8, поэтому любые символы ANSI будут занимать только один байт. Если у вас много строк, ваш размер будет настолько бесполезным.
TMN

1
хотя это может не дать точного размера, для моих нужд мне нужно было только сравнение между 2 объектами и SizeOf не будет инициализироваться из веб-приложения. Спасибо!
Исаак

1
Хорошая рекомендация YourKit . Другие альтернативы - VirtualVM и jvmmonitor
angelcervera,

38

Если вы просто хотите узнать, сколько памяти используется в вашей 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 МБ памяти».


24
Это не очень хорошее решение, так как вы никогда не знаете, когда произойдет сборка мусора или сколько дополнительной памяти будет выделено для кучи сразу.
Ник Фортескью

5
Это правда, и я бы не собирался решать этот главный вопрос этого поста, но это может помочь ему программно узнать, когда он приближается к достижению максимального размера кучи.
Matt B

1
Другая проблема этого решения - когда вы находитесь в многопоточной среде (например, на веб-сервере). Возможно, что другие потоки были в исполнении и потребляли память. В этом приближении вы рассчитываете используемую память во всей виртуальной машине.
Angelcervera

1
Еще одним недостатком является то, что freeMemory возвращает приближение. Попробуйте создать объект javax.crypto.Cipher. Разница между двумя вызовами freeMemory (для оценки размера шифра) не постоянна!
Евгений

1
Я считаю, что вы можете форсировать сборку мусора, так что вы можете сделать некоторые вещи в этом подходе.
matanster

24

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

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


1
Szia! Я также хотел бы выкрикнуть вашу презентацию : слайды 15–20 великолепно помогают инстинктивно ощутить стоимость различных решений по структуре данных. Спасибо за публикацию этого!
Люк Ашервуд

16
«у него нет внешних зависимостей» - с каких пор гуава не является внешней зависимостью?
14

2
выглядит очень похоже на github.com/JetBrains/jdk8u_nashorn/blob/master/src/jdk/nashorn/… ? : O
Франческо

Guave - это внешняя зависимость.
Мерт Сеример

18

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

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

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

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


11

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

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

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


Разве размеры различных примитивов не определены в спецификации Java? (§2.4.1)
erickson

4
Не в смысле «сколько памяти это занимает», вот в чем вопрос. Только в том смысле, как они работают. Например, байты, символы и шорты занимают целое слово в стеке Java, даже если они работают с округлением и т. Д.
Джейсон Коэн,

1
Это похоже на измерение размера, как показано Хайнцем в его бюллетене № 78: javaspecialists.eu/archive/Issue078.html . Я использовал это. Его подход работает.
Питер Кофлер

8

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

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

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

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

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


@erickson Я не уверен насчет sizeof (логическое значение) == 1, просматривающего эту ветку ( stackoverflow.com/questions/1907318/… ). Можете ли вы прокомментировать это?
dma_k

2
@dma_k, Java не имеет настоящих логических значений. Размер логического значения составляет 4 байта вне массивов и 1 байт внутри boolean[]. На самом деле все примитивы не двойного / длинного типа имеют размер 4 байта. Последние 8 (ответ ошибочно ставит их как 4 тоже)
bestsss

@bestsss: Точнее, минимальное распределение памяти зависит от платформы и реализации JVM. Также объекты в куче выровнены, поэтому после суммирования всех размеров нужно округлить.
dma_k

6

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

Интересный подход, но разве это не предполагает, что объект и хранилище его полей не фрагментированы?
nicoulaj

Да, и я не знаю никакой реализации JVM, которая делает такую ​​фрагментацию.
Мигель Гамбоа

Я не понимаю Фрагментация не вариант :) Давайте рассмотрим пример объекта C, который хранится как поле объектов A и B. Разве это не сдвигает все это в A или B?
nicoulaj

Извините, но я не понимаю вашу точку зрения. Согласно моей интерпретации, объекты Java не могут храниться внутри других объектов, как это происходит со структурами C или типами значений в .Net. Поэтому, когда вы говорите: «объект C, который хранится как поле объектов A и B», это означает, что у объектов A и B есть поля, в которых хранятся ссылки (указатели) на объект C. Тогда размеры A и B равны смещение этого поля плюс размер ссылки (указателя) на объект C. И размер ссылки равен размеру одного слова.
Мигель Гамбоа

О, хорошо, мы говорим о мелком размере. Виноват.
nicoulaj

6

Существует также инструмент Memory Measurer (ранее в Google Code , теперь в GitHub ), который прост и опубликован под коммерческой лицензией Apache 2.0 , как обсуждалось в аналогичном вопросе .

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


4

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

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

do your job here

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

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

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


5
Разве не System.gc()просто уведомить, что вы хотите GC? Не гарантируется, что GC вызывается вообще.
Raildex

@действительно мило. Это небезопасно, потому что вы никогда не можете делать то, что GC делает или влияет на память между вашими строками. Таким образом, «между» двумя методами freeMemory GC может высвободить больше места, которое вы не считаете, таким образом, ваш объект будет выглядеть меньше
Mert Serimer

@MertSerimer «небезопасно» для меня на совершенно другом уровне: в большинстве случаев это не так точно, как я уже говорил. Кроме того, вы не можете управлять GC (как заявлено Raildex), но и для этого случая я предложил вставить это в цикл. Это просто быстрая и грязная и приблизительная система, которая работает, если результат не должен быть очень надежным, как указано.
действительно хорошая

Есть много проблем с этим, но это дает вам хорошую добычу.
Markthegrea

3

Вот утилита, которую я сделал, используя некоторые из связанных примеров для обработки 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 );
    }
}

Вы проверяли этот класс со значениями? Я пытался, но для меня неверные значения !!!.
Дебора

1
Значения, которые он дал мне для простого объекта, были примерно правильными, но с коэффициентом 10 для списка, содержащего 1 миллион объектов. Тем не менее, очень хорошая работа!
Майкл Беклинг

Интересно. Я протестировал его, используя JDK7u67, в Windows 7 x64 и Linux 2.6.16 / x86_64, используя каждый из 32-битных / 64-битных / oop режимов адресации. Я сравнил это с дампами памяти, проанализированными в Eclipse Memory Analyzer 1.3.x. Какие настройки вы используете? У вас есть конкретный пример, который я мог бы попробовать?
dlaudams

Лучший выбор, который я могу сделать. Я не могу использовать, Instrumentationпотому что я не запускаю tomcat, ObjectSizeCalculatorпотому что не уверен в типе VM (HotSpot) и JOLбобовых весенних бобах. Я использую это и добавляю второй параметр для игнорирования синглетонов, а именно код AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()рефакторинга, internalSizeOfчтобы игнорировать Class и Enum
Perlos

Для сравнения результатов используйте ObjectSizeCalculator (вычислить весь сервер от 1 ГБ до 10 с). JOL вызывает MemError (6GB не должно быть достаточно), и я не получаю те же результаты, вероятно, потому что перечисления.
Perlos

3

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

  • Доступно во время выполнения без необходимости включать инструментарий.
  • Работает с Java 9+ без доступа к Unsafe.
  • Основан только на классе. Не глубокий sizeOf, который учитывает длину строки, длину массива и т. Д.

Нижеследующее основано на основном коде исходной статьи специалистов по Java ( https://www.javaspecialists.eu/archive/Issue078.html ) и нескольких фрагментах небезопасной версии в другом ответе на этот вопрос.

Я надеюсь, что кто-то найдет это полезным.

public class JavaSize {

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 HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}


2

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

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


2

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

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

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

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


2

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

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

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


2

При использовании JetBrains IntelliJ сначала включите «Присоединить агент памяти» в меню «Файл» | Настройки | Сборка, выполнение, развертывание | Debugger.

При отладке щелкните правой кнопкой мыши интересующую переменную и выберите «Рассчитать оставшийся размер»: Рассчитать оставшийся размер


1

Мой ответ основан на коде, предоставленном Ником. Этот код измеряет общее количество байтов, которые заняты сериализованным объектом. Так что это фактически измеряет материал сериализации + объем памяти обычного объекта (просто сериализуйте, например, 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 и на некоторых тривиальных классах. Также могут быть не охваченные случаи.


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


0

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


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

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

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


что если GC работает посередине во время // кода для построения объекта? Мог бы теперь дать правильный результат все время.
Раджугааду

0

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

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

Сейчас 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; 
}

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

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

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 байт) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 байт)


1
Как может 4-байтовое целое число и ссылка на объект неизвестного размера вписаться в 4 байта?
маркиз Лорн

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

0

Предположим, я объявил класс с именем 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

-5

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

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

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

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


это будет работать только для объектов, которые в основном являются строками.
Декстер Легаспи

-6

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

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

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

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

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


3
Я бы сериализовал его в byte [], используя ByteArrayOutputStream. Это было бы намного быстрее, чем записать его в файл.
ScArcher2

@KorayTugay Определение размера объекта в байтах уже является дорогостоящей операцией. Запись каждого объекта на диск для определения размера, просто заставит его ползти ...
HammerNL

1
Формат сериализованного объекта полностью отличается от формата объекта в куче памяти. В частности, дескриптор класса объекта (и всех его сериализуемых суперклассов) записывается в поток. Таким образом, написание простого экземпляра java.lang.Integerдает около 80 байтов, где представление кучи обычно равно 32 (в отличие от представления потока объекта, представление кучи зависит от размеров указателя и выравнивания объекта). Напротив, для сериализованной nullссылки требуется один байт вместо четырех или восьми байтов в памяти кучи.
Хольгер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.