На Java, який найкращий спосіб визначити розмір об'єкта?


616

У мене є додаток, який читає файл CSV з купами рядків даних. Я даю користувачеві підсумок кількості рядків на основі типів даних, але хочу переконатися, що я не читаю в занадто багато рядків даних і викликаю OutOfMemoryErrors. Кожен рядок перекладається на об’єкт. Чи є простий спосіб визначити розмір об'єкта програмно? Чи є посилання, яке визначає, наскільки великі примітивні типи та посилання на об'єкти для VM?

На даний момент у мене є код, який говорить про читання до 32 000 рядків , але я також хотів би мати код, який говорить, що читати якомога більше рядків, поки я не використаю 32 Мб пам'яті. Можливо, це вже інше питання, але я все одно хотів би знати.


Я додав мій агент з МВН конфіги і пояснив , як тут: stackoverflow.com/a/36102269/711855
juanmf

Відповіді:


461

Ви можете використовувати пакет 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
@Stefan Приємний натяк! Чи можете ви сказати, якою буде розмір byte[0], byte[1], byte[5], int[0], int[1], int[2]використовуючи підхід , ви описали? Було б добре, якби результати включали накладні витрати на довжину масиву та вирівнювання пам’яті.
dma_k

8
Я спробував це і отримав дивні та непосидні результати. Струни завжди були 32, незалежно від розміру. Я подумав, що це, можливо, розмір вказівника, але для іншого непорушного класу, який я створив, я отримав 24. Він добре працює для примітивів, але тоді вам не потрібна програма, яка б розповіла вам, наскільки велика гра.
Брель

6
@Brel це рішення є лише "наближенням обсягу зберігання, споживаного зазначеним об'єктом", як зазначено в документації. Також я припускаю, що автори вирішили встановити розмір String як 32 байти (лише вказівник?) Через пул String Java, що ускладнює питання про те, поділяється екземпляр String (зберігається в пулі) або місцеві та унікальні для класу.
Андрій І

11
Як я можу користуватися ObjectSizeFetcher, якщо не експортувати банку? У мене є тестовий проект java в затемненні.
Юра Шинкарев

3
@brel Причина String - це лише 32 байти незалежно від фактичної довжини, тому що частина рядка змінної довжини зберігається у char [], що є її власним об'єктом. Щоб отримати справжній розмір об'єкта, потрібно додати розмір себе та розмір кожного об'єкта, на який він посилається.
tombrown52

116

Вам слід використовувати 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(). Звичайно, деякі об’єкти в сліді можуть бути спільними (також посилаються на інші об'єкти), тому це надмірне збільшення площі, яку можна було б відновити, коли цей об'єкт збирається сміттям. Для результату 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
Ця відповідь повинна мати більше відгуків. Безумовно, дуже хороший варіант перевірки. EDIT: Перевірили, що це було додано цього року, тоді як питання було задано у08 році. Напевно, найкращий і найпростіший варіант зробити те, про що в даний момент попросили ОП.
орендує

4
Автор інструменту написав публікацію в блозі про Jol .
Майк

2
Для визначення розміру об'єкта "obj" використовуйте: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
бадьорість

Зауважте, що vmDetailsзараз VM.current().details().
Miha_x64

Перевірте, GraphLayout.parseInstance(instance).toFootprint()я вважаю кориснішим зрозуміти розміри об'єктів
Mugen

81

Я випадково знайшов клас 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 в рамках проекту Нашорн . Однак я не знайшов жодної офіційної документації щодо цього класу в Інтернеті.
Анріке Гонтійо

Здається, не враховувати довжину рядків. Це лише розмір у стеці?
jontejj

1
У мене є хеш-карта, де com.carrotsearch.RamUsageEstimator повертає приблизно половину ObjectSizeCalculator. Яка з них справжня? - Хто з них легший?
badera

9
Зауважте, що ObjectSizeCalculatorпідтримується лише на HotSpot VM
kellanburket

74

Деякі роки тому у Javaworld була стаття про визначення розміру складених та потенційно вкладених Java-об'єктів , вони в основному проходять через створення реалізації sizeof () на Java. Цей підхід, в основному, ґрунтується на іншій роботі, де люди експериментально визначали розмір примітивів та типових об’єктів Java, а потім застосовують ці знання до методу, який рекурсивно використовує графік об'єкта, щоб підрахувати загальний розмір.

Це завжди буде дещо менш точним, ніж рідне впровадження C просто через те, що відбувається за лаштунками класу, але це має бути хорошим показником.

Крім того, проект SourceForge належним чином називається sizeof, який пропонує бібліотеку Java5 з реалізацією sizeof ().

PS Не використовуйте підхід серіалізації, немає кореляції між розміром серіалізованого об'єкта та кількістю пам'яті, яку він споживає під час прямої трансляції.


6
Утиліта sizeof - це, мабуть, найшвидший спосіб. Це в основному те, що сказав Стефан, але вже упакований в банку, готовий до вживання.
Олександр L Теллес

62

По-перше, "розмір об'єкта" не є чітко визначеною концепцією в 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 до значущого значення.


2
Мені подобається такий підхід. Як далеко за розміром об'єкта ви відмовилися.
Берлін Браун

1
Дуже просто і ефективно. Інші методи занадто безладні (особливо всередині Eclipse RCP). Дякую.
марколопи

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
Це правда, і я б не мав наміру це вирішувати головне питання цієї посади, але це може допомогти йому зрозуміти програмно, коли він стає дещо близьким до досягнення максимального розміру купи.
мат б

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
"вона не має зовнішніх залежностей" - оскільки коли гуава не є зовнішньою залежністю?
l4mpi


Гуаве - це зовнішня залежність.
Мерт Серімер

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або щось подібне, використовуючи object-equals як компаратор для усунення нескінченних циклів.

@jodonnell: Мені подобається простота вашого рішення, але багато об'єктів не є серіалізаційними (тому це може кинути виняток), поля можуть бути перехідними, а об'єкти можуть заміняти стандартні методи.


Чи не визначені розміри різних примітивів у специфікації Java? (§2.4.1)
еріксон

4
Не в сенсі "скільки пам’яті займає", в чому питання. Тільки в сенсі того, як вони діють. Наприклад, байти, символи та шорти займають ціле слово на стеці Java, навіть якщо вони оперують округленням тощо.
Jason Cohen

1
Це схоже на вимірювання розміру, як показав Хайнц у своєму бюлетені № 78: javaspecialists.eu/archive/Issue078.html . Я ним користувався. Його підхід працює.
Пітер Кофлер

8

Ви повинні виміряти його інструментом або оцінити вручну, і це залежить від JVM, який ви використовуєте.

На деякий об’єкт є фіксований накладний наклад. Це специфічно для JVM, але я зазвичай оцінюю 40 байт. Тоді ви повинні подивитися на членів класу. Посилання на об'єкти - це 4 (8) байт у 32-розрядному (64-бітовому) JVM. Примітивними типами є:

  • булева та байтова: 1 байт
  • char та коротка: 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. Чи не переміщує це все в А або В?
nicoulaj

Вибачте, але я не розумію вашої точки зору. Згідно з моєю інтерпретацією, об'єкти Java не можуть зберігатися всередині інших об'єктів, як це відбувається зі структурами C або типами значення в .Net. Отже, коли ви говорите: "об'єкт C, який зберігається як поле об'єктів A і B", це означає, що об'єкти A і B мають поля, які зберігають посилання (покажчики) на об'єкт C. Тоді розміри A і B дорівнюють зміщення цього поля плюс розмір посилання (вказівника) на об’єкт C. І розмір посилання - це розмір одного слова.
Мігель Гамбоа

О, гаразд, ми говоримо про неглибокі розміри. Моє ліжко.
nicoulaj

6

Існує також інструмент « Меректор пам’яті » (раніше в 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

@reallynice. Це не є безпечним, оскільки ви ніколи не можете робити те, що GC робить або не впливає на пам'ять між лініями. Тож "між" двома методами freeMemory GC може звільнити більше місця, яке ви не вважаєте, таким чином ваш об'єкт буде виглядати менше
Mert Serimer

@MertSerimer "не безпечний" для мене зовсім інший рівень: як мінімум, це не так точно, як я також заявив. Крім того, ви не можете керувати GC (як заявлено Raildex), але і в цьому випадку я запропонував вставити це в цикл. Це просто швидка та брудна та приблизна система, яка працює, якщо результат не потрібно бути дуже надійним, як заявлено.
realnice

З цим виникає багато проблем, але це дає хороший замах.
marthegrea

3

Ось утиліта, яку я зробив, використовуючи деякі зв'язані приклади для обробки 32-розрядної, 64-розрядної та 64-розрядної версії зі стиснутим OOP. Він використовує 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 разів для списку, що містить 1mio об'єктів. Все-таки дуже приємна робота!
Майкл Бёклінг

Цікаво. Я протестував його за допомогою 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 and Enum
Perlos

Для порівняння результатів використовуйте ObjectSizeCalculator (Обчисліть весь сервер від 1 ГБ до 10 с). JOL викликає MemError (6 Гб не буде сприйматися), і я не отримую однакових результатів, ймовірно, тому, що перераховує.
Перлос

3

Я шукав розрахунок часу виконання об'єкта, який відповідає наступним вимогам:

  • Доступний під час виконання без необхідності включати приладобудування.
  • Працює з Java 9+ без доступу до Unsafe.
  • Базується лише на класі. Не глибокий розмірOf, який враховує довжину рядків, довжину масиву тощо.

Далі на основі основного коду оригінальної статті фахівців Java ( https://www.javaspecialists.eu/archive/Issue078.html ) та декількох бітів з версії Unsafe в іншій відповіді на це питання.

Сподіваюся, хтось вважає це корисним.

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)


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 та на деяких тривіальних класах. Також можуть бути не охоплені справи.


ОНОВЛЕННЯ: Приклад модифікований для підтримки обчислення пам'яті об'єктів масиву.


1

Використовуючи JetBrains IntelliJ, спочатку увімкніть "Вкласти агент пам'яті" у файл | Налаштування | Збірка, виконання, розгортання | Налагоджувач.

Під час налагодження правою кнопкою миші клацніть змінну, що цікавить, та виберіть "Розрахувати нерозмірний розмір": Обчисліть нерозмірний розмір


0

Ви можете згенерувати накопичувальний накопичувач (наприклад, з jmap), а потім проаналізувати вихід, щоб знайти розміри об'єктів. Це офлайн-рішення, але ви можете вивчити дрібні та глибокі розміри тощо.


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

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

розмір дає вам збільшення використання пам'яті jvm в пам'яті за рахунок створення об'єкта, і це, як правило, розмір об'єкта.


що робити, якщо GC працює в середині під час // Код для побудови об'єкта? Тепер, можливо, весь час давати правильний результат.
rajugaadu

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.

Я маю на увазі сказати, що для всього ОБ'ЯЗАННЯ об'єкта потрібно лише 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 байт) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 байт)


1
Як 4-байтне ціле число та посилання на об'єкт невідомого розміру вміщуються в 4 байти?
Маркіз Лорнський

@EJP Я маю на увазі сказати, що для всього ОБ'ЯЗАННЯ об'єкта потрібно лише 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

-4

Для 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 ();

Звичайно, це передбачає, що кожен об'єкт є виразним і не містить непрохідних посилань ні на що інше.

Іншою стратегією було б взяти кожен об’єкт і вивчити його членів шляхом відображення та скласти розміри (булева & байт = 1 байт, коротка & char = 2 байти тощо), працюючи на шляху до ієрархії членства. Але це нудно і дорого, і в кінцевому підсумку робиться те саме, що і стратегія серіалізації.


3
Я серіалізував би його в байт [] за допомогою ByteArrayOutputStream. Це було б набагато швидше, ніж записувати його у файл.
ScArcher2

@KorayTugay Визначення розміру байта об'єкта - це вже дорога операція. Записавши кожен предмет на диск, щоб визначити розмір, це просто змусить його повзати ...
HammerNL

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