В Java, как лучше всего определить размер объекта? - PullRequest
568 голосов
/ 09 сентября 2008

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

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

Ответы [ 24 ]

438 голосов
/ 09 сентября 2008

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

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

71 голосов
/ 09 сентября 2008

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

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

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

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

59 голосов
/ 09 сентября 2008

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

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

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

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

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

58 голосов
/ 09 сентября 2016

Я случайно нашел класс 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
35 голосов
/ 09 сентября 2008

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

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

20 голосов
/ 09 апреля 2015

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

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

13 голосов
/ 06 марта 2015

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

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

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

10 голосов
/ 09 сентября 2008

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

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

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

8 голосов
/ 09 сентября 2008

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...