Di Jawa, apa cara terbaik untuk menentukan ukuran suatu objek?


617

Saya memiliki aplikasi yang membaca file CSV dengan tumpukan baris data. Saya memberi pengguna ringkasan jumlah baris berdasarkan jenis data, tetapi saya ingin memastikan bahwa saya tidak membaca terlalu banyak baris data dan penyebabnya OutOfMemoryError. Setiap baris diterjemahkan menjadi objek. Apakah ada cara mudah untuk mengetahui ukuran objek itu secara terprogram? Apakah ada referensi yang menentukan seberapa besar tipe primitif dan referensi objek untuk suatu VM?

Saat ini, saya memiliki kode yang mengatakan membaca hingga 32.000 baris , tetapi saya juga ingin memiliki kode yang mengatakan membaca sebanyak mungkin baris sampai saya menggunakan memori 32MB . Mungkin itu pertanyaan yang berbeda, tapi saya masih ingin tahu.


Saya menambahkan Agen saya dengan mvn konfigurasi dan menjelaskan caranya di sini: stackoverflow.com/a/36102269/711855
juanmf

Jawaban:


460

Anda dapat menggunakan paket java.lang.instrument

Kompilasi dan letakkan kelas ini dalam 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);
    }
}

Tambahkan yang berikut ke Anda MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Gunakan getObjectSize:

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

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

Diminta dengan:

java -javaagent:ObjectSizeFetcherAgent.jar C

2
@Stefan Petunjuk yang bagus! Bisa anda jelaskan, apa yang akan menjadi ukuran byte[0], byte[1], byte[5], int[0], int[1], int[2]menggunakan pendekatan yang Anda dijelaskan? Akan lebih baik, jika hasilnya termasuk overhead untuk panjang array dan penyelarasan memori.
dma_k

8
Saya mencoba ini dan mendapat hasil yang aneh dan tidak membantu. String selalu 32, terlepas dari ukurannya. Saya pikir ini mungkin ukuran pointer tetapi untuk kelas yang saya buat tidak berubah, saya mendapat 24. Ini berfungsi dengan baik untuk primitif tetapi kemudian Anda tidak benar-benar membutuhkan program untuk memberi tahu Anda seberapa besar char.
Brel

6
@ Larangkan solusi ini hanya "perkiraan jumlah penyimpanan yang dikonsumsi oleh objek yang ditentukan", sebagaimana ditentukan dalam dokumentasi. Juga saya kira bahwa penulis memutuskan untuk mengatur ukuran String sebagai 32 byte (hanya pointer?) Karena kumpulan String Java, yang membuatnya sulit untuk mengatakan, apakah instance String dibagi (disimpan di pool) atau lokal & unik untuk sebuah kelas.
Andrei I

11
Bagaimana saya bisa menggunakan ObjectSizeFetcher, jika tidak mengekspor toples? Saya telah menguji proyek java di gerhana.
Yura Shinkarev

3
@brel Alasan sebuah String hanya 32 byte terlepas dari panjang sebenarnya adalah karena bagian panjang variabel dari string disimpan dalam char [], yang merupakan objek itu sendiri. Untuk mendapatkan ukuran sebenarnya dari suatu objek, Anda perlu menambahkan ukuran itu sendiri dan ukuran setiap objek yang dirujuk.
tombrown52

117

Anda harus menggunakan jol , alat yang dikembangkan sebagai bagian dari proyek OpenJDK.

JOL (Java Object Layout) adalah kotak alat kecil untuk menganalisis skema tata letak objek di JVM. Alat-alat ini menggunakan Unsafe, JVMTI, dan Serviceability Agent (SA) untuk men-decoder tata letak objek, jejak, dan referensi yang sebenarnya. Ini membuat JOL jauh lebih akurat daripada alat lain yang mengandalkan heap dumps, asumsi spesifikasi, dll.

Untuk mendapatkan ukuran primitif, referensi, dan elemen array, gunakan VMSupport.vmDetails(). Pada Oracle JDK 1.8.0_40 berjalan pada Windows 64-bit (digunakan untuk semua contoh berikut), metode ini kembali

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]

Anda bisa mendapatkan ukuran dangkal dari instance objek menggunakan ClassLayout.parseClass(Foo.class).toPrintable()(opsional lewat instance ke toPrintable). Ini hanya ruang yang dikonsumsi oleh satu instance dari kelas itu; itu tidak termasuk objek lain yang dirujuk oleh kelas itu. Itu tidak termasuk overhead VM untuk header objek, keselarasan bidang dan padding. Untuk 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

Anda bisa mendapatkan tampilan ringkasan ukuran mendalam dari instance objek menggunakan GraphLayout.parseInstance(obj).toFootprint(). Tentu saja, beberapa objek dalam tapak mungkin dibagikan (juga direferensikan dari objek lain), sehingga ini merupakan perkiraan berlebihan dari ruang yang dapat direklamasi ketika objek tersebut adalah sampah yang dikumpulkan. Untuk hasil Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(diambil dari jawaban ini ), jol melaporkan jejak total 1840 byte, di mana hanya 72 adalah contoh Pola itu sendiri.

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)

Jika Anda sebaliknya menggunakan GraphLayout.parseInstance(obj).toPrintable(), jol akan memberi tahu Anda alamat, ukuran, jenis, nilai, dan jalur dereferensi bidang untuk setiap objek yang direferensikan, meskipun itu biasanya terlalu banyak detail untuk berguna. Untuk contoh pola yang sedang berlangsung, Anda mungkin mendapatkan yang berikut ini. (Alamat kemungkinan akan berubah antar berjalan.)

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)

Entri "(sesuatu yang lain)" menggambarkan objek lain di heap yang bukan bagian dari grafik objek ini .

Dokumentasi jol terbaik adalah sampel jol dalam repositori jol. Sampel menunjukkan operasi jol umum dan menunjukkan bagaimana Anda dapat menggunakan jol untuk menganalisis VM dan internal pengumpul sampah.


18
Jawaban ini seharusnya memiliki lebih banyak suara positif. Pilihan yang sangat bagus untuk diperiksa. EDIT: Memeriksa apakah ini ditambahkan tahun ini sementara pertanyaan diajukan pada '08. Mungkin opsi terbaik dan termudah untuk melakukan apa yang diminta OP saat ini.
sewa

4
Penulis alat menulis posting blog tentang Jol .
Mike

2
Untuk menentukan ukuran Object "obj" gunakan: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
vigor

Perhatikan itu vmDetailssekarang VM.current().details().
Miha_x64

Lihat, GraphLayout.parseInstance(instance).toFootprint()saya merasa lebih berguna untuk memahami ukuran objek
Mugen

82

Saya tidak sengaja menemukan kelas java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", sudah di jdk, yang mudah digunakan dan tampaknya cukup berguna untuk menentukan ukuran suatu objek.

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

hasil:

164192
48
16
48
416

3
Sama di sini, saya mencoba solusi lain yang diusulkan di atas dan menemukan ObjectSizeCalculator. Saya percaya tidak ada yang menyebutkan jika sebelumnya sejak baru-baru ini diperkenalkan pada JDK 8 sebagai bagian dari proyek Nashorn . Namun saya belum menemukan dokumentasi resmi tentang kelas ini di web.
Henrique Gontijo

Tampaknya tidak mempertimbangkan panjang tali. Apakah hanya tentang ukuran pada tumpukan?
jontejj

1
Saya memiliki hashmap, di mana com.carrotsearch.RamUsageEstimator mengembalikan sekitar setengah dari ObjectSizeCalculator. Mana yang benar? - Yang mana yang lebih bisa diandalkan?
badera

9
Catatan yang ObjectSizeCalculatorhanya didukung pada HotSpot VM
kellanburket

74

Beberapa tahun yang lalu Javaworld memiliki artikel tentang menentukan ukuran komposit dan objek Java yang berpotensi bersarang , mereka pada dasarnya berjalan melalui pembuatan sizeof () implementasi di Jawa. Pendekatan ini pada dasarnya dibangun di atas karya lain di mana orang secara eksperimental mengidentifikasi ukuran primitif dan objek Java yang khas dan kemudian menerapkan pengetahuan itu ke metode yang secara rekursif berjalan grafik objek untuk menghitung ukuran total.

Itu selalu akan menjadi kurang akurat daripada implementasi C asli hanya karena hal-hal yang terjadi di belakang layar kelas tetapi itu harus menjadi indikator yang baik.

Atau proyek SourceForge tepat disebut sizeof yang menawarkan perpustakaan Java5 dengan sizeof () implementasi.

PS Jangan menggunakan pendekatan serialisasi, tidak ada korelasi antara ukuran objek serial dan jumlah memori yang dikonsumsi saat hidup.


6
Utilitas sizeof mungkin cara tercepat. Pada dasarnya itu yang dikatakan Stefan, tapi sudah dikemas dalam toples yang siap digunakan.
Alexandre L Telles

62

Pertama "ukuran objek" bukan konsep yang didefinisikan dengan baik di Jawa. Anda dapat memaksudkan objek itu sendiri, hanya dengan anggota-anggotanya, Obyek dan semua objek yang dirujuknya (grafik referensi). Anda bisa mengartikan ukuran dalam memori atau ukuran pada disk. Dan JVM diizinkan untuk mengoptimalkan hal-hal seperti Strings.

Jadi satu-satunya cara yang benar adalah bertanya pada JVM, dengan profiler yang bagus (saya menggunakan YourKit ), yang mungkin bukan yang Anda inginkan.

Namun, dari uraian di atas sepertinya setiap baris akan mandiri, dan tidak memiliki pohon dependensi yang besar, sehingga metode serialisasi mungkin akan menjadi pendekatan yang baik pada sebagian besar JVM. Cara termudah untuk melakukan ini adalah sebagai berikut:

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

Ingat bahwa jika Anda memiliki objek dengan referensi umum, ini tidak akan memberikan hasil yang benar, dan ukuran serialisasi tidak akan selalu cocok dengan ukuran dalam memori, tetapi merupakan perkiraan yang baik. Kode akan sedikit lebih efisien jika Anda menginisialisasi ukuran ByteArrayOutputStream ke nilai yang masuk akal.


2
Saya suka pendekatan ini. Seberapa jauh dalam hal ukuran objek Anda telah pergi.
Berlin Brown

1
Sangat sederhana dan efektif. Metode lain terlalu berantakan (khususnya di dalam Eclipse RCP). Terima kasih.
marcolopes

19
Serialisasi tidak akan melacak variabel sementara, dan metode serialisasi default menulis string dalam UTF-8, sehingga setiap karakter ANSI hanya akan mengambil satu byte. Jika Anda memiliki banyak string, ukuran Anda akan sangat jauh sehingga tidak berguna.
TMN

1
sementara ini mungkin tidak memberikan ukuran yang tepat, untuk kebutuhan saya, saya hanya perlu perbandingan antara 2 objek dan SizeOf tidak akan menginisialisasi dari aplikasi web. Terima kasih!
Isaac

1
Rekomendasi bagus dari YourKit . Alternatif lain adalah VirtualVM dan jvmmonitor
angelcervera

38

Jika Anda hanya ingin tahu berapa banyak memori yang digunakan dalam JVM Anda, dan berapa banyak yang gratis, Anda dapat mencoba sesuatu seperti ini:

// 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();

sunting: Saya pikir ini mungkin membantu karena penulis pertanyaan juga menyatakan dia ingin memiliki logika yang menangani "baca sebanyak mungkin baris sampai saya menggunakan memori 32MB."


24
Ini bukan solusi yang baik, karena Anda tidak pernah tahu kapan pengumpulan sampah akan terjadi, atau berapa banyak memori tambahan yang akan dialokasikan ke tumpukan sekaligus.
Nick Fortescue

5
Itu benar, dan saya tidak akan bermaksud untuk menjawab pertanyaan utama dari posting ini, tetapi mungkin membantunya untuk mengetahui secara pemrograman ketika dia semakin dekat untuk mencapai ukuran tumpukan maksimum.
matt b

1
Masalah lain dari solusi ini adalah ketika Anda berada di lingkungan multi-thread (seperti di server web). Ada kemungkinan bahwa utas lain dalam eksekusi dan menghabiskan memori. Dengan perkiraan ini Anda menghitung memori yang digunakan di semua mesin virtual.
angelcervera

1
Kerugian lain adalah bahwa freeMemory mengembalikan perkiraan. Coba buat objek javax.crypto.Cipher. Perbedaan antara dua panggilan ke freeMemory (untuk memperkirakan ukuran Cipher) tidak konstan!
Eugen

1
Saya percaya Anda dapat memaksa pengumpulan sampah, sehingga Anda dapat melakukan beberapa hal dalam pendekatan ini.
matanster

24

Kembali ketika saya bekerja di Twitter, saya menulis sebuah utilitas untuk menghitung ukuran objek yang dalam. Ini memperhitungkan model memori yang berbeda (32-bit, oops terkompresi, 64-bit), padding, subclass padding, berfungsi dengan benar pada struktur data array dan array. Anda bisa mengkompilasi file .java yang satu ini; tidak memiliki dependensi eksternal:

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


1
Szia! Saya hanya ingin berteriak presentasi juga: slide 15-20 sangat bagus untuk membantu mendapatkan perasaan naluriah untuk biaya berbagai keputusan struktur data. Terima kasih telah memposting itu!
Luke Usherwood

16
"ia tidak memiliki ketergantungan eksternal" - sejak kapan jambu biji bukan ketergantungan eksternal?
14mpi


Guave adalah ketergantungan eksternal.
Mert Serimer

18

Sebagian besar jawaban lain memberikan ukuran yang dangkal - misalnya ukuran HashMap tanpa kunci atau nilai apa pun, yang sepertinya tidak sesuai dengan yang Anda inginkan.

Proyek jamm menggunakan paket java.lang.instrumentation di atas tetapi berjalan pohon dan sehingga dapat memberi Anda penggunaan memori yang mendalam.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Untuk menggunakan MemoryMeter, mulai JVM dengan "-javaagent: /jamm.jar"


11

Anda harus berjalan di objek menggunakan refleksi. Hati-hati seperti yang Anda lakukan:

  • Hanya mengalokasikan objek memiliki beberapa overhead di JVM. Jumlahnya bervariasi menurut JVM sehingga Anda dapat menjadikan nilai ini sebagai parameter. Setidaknya membuatnya konstan (8 byte?) Dan berlaku untuk apa pun yang dialokasikan.
  • Hanya karena bytesecara teoritis 1 byte tidak berarti hanya membutuhkan satu memori.
  • Akan ada loop dalam referensi objek, jadi Anda harus menyimpan HashMapatau kira - kira menggunakan objek-sama sebagai pembanding untuk menghilangkan loop tak terbatas.

@jodonnell: Saya suka kesederhanaan solusi Anda, tetapi banyak objek tidak Serializable (jadi ini akan melempar pengecualian), bidang dapat bersifat sementara, dan objek dapat menimpa metode standar.


Bukankah ukuran berbagai primitif didefinisikan dalam Spesifikasi Java? (§2.4.1)
erickson

4
Bukan dalam arti "berapa banyak memori yang dihuni," yang merupakan pertanyaan. Hanya dalam arti bagaimana mereka beroperasi. Sebagai contoh, byte, karakter, dan celana pendek mengambil seluruh kata di tumpukan Java, meskipun mereka beroperasi dengan pembulatan dll.
Jason Cohen

1
Ini terdengar mirip dengan mengukur ukuran, seperti yang ditunjukkan oleh Heinz dalam Newsletter-nya # 78: javaspecialists.eu/archive/Issue078.html . Saya menggunakannya. Pendekatannya berhasil.
Peter Kofler

8

Anda harus mengukurnya dengan alat, atau memperkirakannya dengan tangan, dan itu tergantung pada JVM yang Anda gunakan.

Ada beberapa overhead tetap per objek. Ini spesifik JVM, tapi saya biasanya memperkirakan 40 byte. Maka Anda harus melihat anggota kelas. Referensi objek adalah 4 (8) byte dalam JVM 32-bit (64-bit). Jenis-jenis primitif adalah:

  • boolean dan byte: 1 byte
  • char dan pendek: 2 byte
  • int dan float: 4 byte
  • panjang dan ganda: 8 byte

Array mengikuti aturan yang sama; artinya, ini adalah referensi objek sehingga membutuhkan 4 (atau 8) byte di objek Anda, dan kemudian panjangnya dikalikan dengan ukuran elemennya.

Mencoba melakukannya secara terprogram dengan panggilan Runtime.freeMemory()saja tidak memberikan Anda banyak akurasi, karena panggilan tidak sinkron ke pengumpul sampah, dll. Membuat profil tumpukan dengan -Xrunhprof atau alat lain akan memberi Anda hasil yang paling akurat.


@erickson Saya tidak akan yakin tentang sizeof (boolean) == 1 melihat utas ini ( stackoverflow.com/questions/1907318/… ). Bisakah Anda mengomentari ini?
dma_k

2
@dma_k, Java sebenarnya tidak memiliki boolean nyata. Ukuran boolean adalah 4bytes di luar array dan 1byte di dalamnya boolean[]. Sebenarnya semua tipe primitif non double / panjang adalah 4 byte. Yang terakhir adalah 8 (jawabannya salah menempatkan mereka sebagai 4 juga)
bestsss

@bestsss: Untuk lebih tepatnya, alokasi memori minimal tergantung pada platform dan implementasi JVM. Juga objek pada heap disejajarkan, jadi setelah merangkum semua ukuran kita perlu mengumpulkan.
dma_k

6

The java.lang.instrument.Instrumentationkelas menyediakan cara yang bagus untuk mendapatkan ukuran Obyek Jawa, tetapi mengharuskan Anda untuk menentukanpremain dan menjalankan program Anda dengan agen java. Ini sangat membosankan ketika Anda tidak membutuhkan agen apa pun dan kemudian Anda harus menyediakan agen Jar palsu untuk aplikasi Anda.

Jadi saya mendapat solusi alternatif menggunakan Unsafekelas dari sun.misc. Jadi, dengan mempertimbangkan penumpukan objek tumpukan sesuai dengan arsitektur prosesor dan menghitung bidang offset maksimum, Anda dapat mengukur ukuran Obyek Java. Dalam contoh di bawah ini saya menggunakan kelas bantu UtilUnsafeuntuk mendapatkan referensi ke sun.misc.Unsafeobjek.

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

Pendekatan yang menarik, tetapi bukankah ini mengandaikan objek dan penyimpanan bidangnya tidak terfragmentasi?
nicoulaj

Ya dan saya tidak tahu implementasi JVM yang membuat fragmentasi seperti itu.
Miguel Gamboa

Saya tidak mengerti. Fragmentasi bukanlah suatu pilihan :) Mari kita ambil contoh objek C yang disimpan sebagai bidang objek A dan B. Bukankah itu menggeser semuanya menjadi A atau B?
nicoulaj

Maaf, tapi saya juga tidak mengerti sudut pandang Anda. Menurut interpretasi saya, di Java objek tidak bisa disimpan di dalam objek lain, seperti yang terjadi dengan struktur C atau Value Type di .Net. Jadi ketika Anda mengatakan: "objek C yang disimpan sebagai bidang objek A dan B" itu berarti bahwa objek A dan B memiliki bidang yang menyimpan referensi (pointer) ke objek C. Kemudian ukuran A dan B sama dengan offset bidang itu ditambah ukuran referensi (pointer) ke objek C. Dan ukuran referensi adalah ukuran satu kata.
Miguel Gamboa

Oh, OK, kita berbicara tentang ukuran yang dangkal. Salahku.
nicoulaj

6

Ada juga alat Pengukur Memori (sebelumnya di Google Code , sekarang di GitHub ), yang sederhana dan diterbitkan di bawah lisensi Apache 2.0 yang ramah komersial , seperti yang dibahas dalam pertanyaan serupa .

Ini juga membutuhkan argumen baris perintah ke penerjemah java jika Anda ingin mengukur konsumsi byte memori, tetapi sebaliknya berfungsi dengan baik, setidaknya dalam skenario saya telah menggunakannya.


4

Tanpa harus mengacaukan instrumentasi dan sebagainya, dan jika Anda tidak perlu mengetahui ukuran byte-tepat dari suatu objek, Anda bisa menggunakan pendekatan berikut:

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

do your job here

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

Dengan cara ini Anda membaca memori yang digunakan sebelum dan sesudah, dan memanggil GC sebelum mendapatkan memori yang digunakan Anda menurunkan "noise" hampir ke 0.

Untuk hasil yang lebih andal, Anda dapat menjalankan pekerjaan Anda n kali, dan kemudian membagi memori yang digunakan dengan n, memperoleh berapa banyak memori yang dibutuhkan oleh satu kali menjalankan. Bahkan lebih, Anda dapat menjalankan semuanya lebih banyak kali dan menghasilkan rata-rata.


5
Tidak System.gc()hanya memberitahukan bahwa Anda ingin GC? Tidak dijamin bahwa GC disebut sama sekali.
Raildex

@benar-benar bagus. Ini tidak aman karena Anda mungkin tidak pernah melakukan apa yang GC lakukan atau memengaruhi memori di antara Anda. Jadi "antara" dua metode gratisMemory GC dapat membebaskan lebih banyak ruang yang tidak Anda pertimbangkan sehingga objek Anda akan terlihat lebih kecil
Mert Serimer

@MertSerimer "tidak aman" ada di seluruh level yang berbeda untuk saya: paling-paling ini tidak begitu akurat, seperti yang juga saya nyatakan. Selain itu, Anda tidak dapat menjalankan GC (seperti yang dinyatakan Raildex), tetapi untuk kasus ini saya juga menyarankan untuk memasukkan ini dalam satu siklus. Ini hanya sistem cepat dan kotor dan perkiraan yang berfungsi jika hasilnya tidak perlu sangat dapat diandalkan, seperti yang dinyatakan.
reallynice

Ada banyak masalah dengan ini tetapi itu memberi Anda barang curian yang baik.
markthegrea

3

Berikut adalah utilitas yang saya buat menggunakan beberapa contoh terkait untuk menangani 32-bit, 64-bit dan 64-bit dengan OOP terkompresi. Itu menggunakan sun.misc.Unsafe.

Ini digunakan Unsafe.addressSize()untuk mendapatkan ukuran pointer asli dan Unsafe.arrayIndexScale( Object[].class )untuk ukuran referensi Java.

Ia menggunakan bidang offset dari kelas yang dikenal untuk menghitung ukuran dasar suatu objek.

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

Apakah Anda menguji kelas ini dengan nilai? Saya mencoba, tetapi bagi saya, nilai yang salah !!!.
Débora

1
Nilai-nilai yang diberikannya kepada saya untuk objek sederhana kira-kira benar, tetapi dimatikan dengan faktor 10 untuk daftar yang berisi objek 1mio. Tetap saja, kerja yang sangat bagus!
Michael Böckling

Menarik. Saya telah mengujinya menggunakan JDK7u67, pada Windows 7 x64 dan Linux 2.6.16 / x86_64, menggunakan masing-masing mode alamat 32bit / 64bit / oop. Saya telah membandingkannya dengan dump memori yang dianalisis dalam Eclipse Memory Analyzer 1.3.x. Pengaturan apa yang Anda gunakan? Apakah Anda memiliki contoh spesifik yang dapat saya coba?
dlaudams

Pilihan terbaik yang bisa saya lakukan. Saya tidak bisa menggunakan Instrumentationkarena saya tidak memulai kucing jantan, ObjectSizeCalculatorkarena tidak yakin jenis VM (HotSpot) dan JOLbacouse spring bean. Saya menggunakan ini dan menambahkan parameter kedua untuk mengabaikan singleton yaitu AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()dan internalSizeOfkode refactor untuk mengabaikan Kelas dan Enum
Perlos

Untuk membandingkan hasil gunakan ObjectSizeCalculator (Hitung seluruh server 1GB hingga 10s). JOL menyebabkan MemError (6GB tidak akan hilang) dan saya tidak mendapatkan hasil yang sama, mungkin karena enum
Perlos

3

Saya mencari perhitungan runtime ukuran objek yang memenuhi persyaratan berikut:

  • Tersedia saat runtime tanpa harus menyertakan instrumentasi.
  • Bekerja dengan Java 9+ tanpa akses ke Tidak Aman.
  • Hanya berdasarkan Kelas. Bukan ukuran yang dalam. Jika mempertimbangkan panjang string, panjang array, dll.

Berikut ini didasarkan pada kode inti dari artikel spesialis java asli ( https://www.javaspecialists.eu/archive/Issue078.html ) dan beberapa bit dari versi Tidak Aman dalam jawaban lain untuk pertanyaan ini.

Saya harap seseorang menemukannya bermanfaat.

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

Tidak ada panggilan metode, jika itu yang Anda minta. Dengan sedikit riset, saya kira Anda bisa menulis sendiri. Sebuah instance tertentu memiliki ukuran tetap yang berasal dari jumlah referensi dan nilai-nilai primitif ditambah data pembukuan instance. Anda cukup berjalan grafik objek. Semakin sedikit variasi jenis baris, semakin mudah.

Jika itu terlalu lambat atau hanya lebih banyak masalah daripada nilainya, selalu ada aturan kuno yang baik.


2

Saya menulis tes cepat sekali untuk memperkirakan dengan cepat:

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

}

Konsep umum adalah mengalokasikan objek dan mengukur perubahan di ruang tumpukan gratis. Kuncinya adalah getFreeMemory(), yang meminta GC berjalan dan menunggu ukuran tumpukan gratis yang dilaporkan untuk stabil . Output di atas adalah:

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

Itulah yang kami harapkan, memberikan perilaku penyelarasan dan kemungkinan tumpukan header blok.

Metode instrumentasi yang dirinci dalam jawaban yang diterima di sini adalah yang paling akurat. Metode yang saya jelaskan ini akurat tetapi hanya dalam kondisi yang terkendali di mana tidak ada utas lain yang membuat / membuang objek.


2

Cukup gunakan java visual VM.

Ini memiliki semua yang Anda butuhkan untuk profil dan men-debug masalah memori.

Ini juga memiliki konsol OQL (Object Query Language) yang memungkinkan Anda untuk melakukan banyak hal berguna, salah satunya adalah sizeof(o)


2

Saat menggunakan JetBrains IntelliJ, pertama-tama aktifkan "Lampirkan agen memori" di File | Pengaturan | Bangun, Eksekusi, Penempatan | Debugger.

Saat debugging, klik kanan variabel yang diinginkan dan pilih "Hitung Ukuran yang Ditahan": Hitung Ukuran yang Ditahan


1

Jawaban saya didasarkan pada kode yang diberikan oleh Nick. Kode itu mengukur jumlah total byte yang ditempati oleh objek berseri. Jadi ini sebenarnya mengukur hal-hal serialisasi + jejak memori objek polos (hanya cerita bersambung misalnya intdan Anda akan melihat bahwa jumlah total byte serial tidak 4). Jadi jika Anda ingin mendapatkan angka byte mentah yang digunakan tepat untuk objek Anda - Anda perlu sedikit memodifikasi kode itu. Seperti itu:

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

}

Saya sudah menguji solusi ini dengan tipe primitif, String, dan pada beberapa kelas sepele. Mungkin tidak ada kasus tertutup juga.


UPDATE: Contoh dimodifikasi untuk mendukung perhitungan jejak memori objek array.


0

Anda bisa menghasilkan heap dump (dengan jmap, misalnya) dan kemudian menganalisis output untuk menemukan ukuran objek. Ini adalah solusi offline, tetapi Anda dapat memeriksa ukuran yang dangkal dan dalam, dll.


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

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

ukuran memberi Anda peningkatan penggunaan memori jvm karena pembuatan objek dan yang biasanya adalah ukuran objek.


bagaimana jika GC berjalan di tengah selama // Code untuk konstruksi objek? Mungkin sekarang menghasilkan hasil yang benar sepanjang waktu.
rajugaadu

0

Jawaban ini tidak terkait dengan ukuran objek, tetapi ketika Anda menggunakan array untuk mengakomodasi objek; berapa ukuran memori yang akan dialokasikan untuk objek.

Jadi array, daftar, atau petakan semua koleksi tidak akan benar-benar menyimpan objek (hanya pada saat primitif, ukuran memori objek nyata diperlukan), ia hanya akan menyimpan referensi untuk objek-objek tersebut.

Sekarang Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 byte) tergantung pada (32/64 bit) OS

PRIMITIF

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

BENDA

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.

Maksud saya mengatakan semua objek REFERENSI hanya membutuhkan 4 byte memori. Mungkin referensi String ATAU referensi objek ganda, tetapi tergantung pada pembuatan objek memori yang dibutuhkan akan bervariasi.

misalnya) Jika saya membuat objek untuk kelas di bawah ini ReferenceMemoryTestmaka 4 + 4 + 4 = 12 byte memori akan dibuat. Memori mungkin berbeda ketika Anda mencoba untuk menginisialisasi referensi.

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

Jadi ketika membuat objek / referensi array, semua isinya akan ditempati dengan referensi NULL. Dan kita tahu setiap referensi membutuhkan 4 byte.

Dan akhirnya, alokasi memori untuk kode di bawah ini adalah 20 byte.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 byte) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 byte)


1
Bagaimana integer 4-byte dan referensi objek dengan ukuran yang tidak diketahui dapat masuk ke dalam 4 byte?
Marquis of Lorne

@ EJP Maksud saya mengatakan semua objek REFERENSI hanya membutuhkan 4 byte memori. Mungkin referensi String ATAU referensi objek ganda, tetapi tergantung pada pembuatan objek memori yang dibutuhkan akan bervariasi.
Kanagavelu Sugumar

0

Misalkan saya mendeklarasikan kelas yang bernama Complexseperti:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Untuk melihat berapa banyak memori yang dialokasikan untuk contoh langsung dari kelas ini:

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

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

-5

Untuk JSONObject, kode di bawah ini dapat membantu Anda.

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

mengembalikan ukuran dalam byte

Saya memeriksanya dengan objek JSONArray saya dengan menulisnya ke file. Ini memberi ukuran objek.


ini hanya akan bekerja untuk objek yang sebagian besar adalah string.
Dexter Legaspi

-6

Saya ragu Anda ingin melakukannya secara terprogram kecuali Anda hanya ingin melakukannya sekali dan menyimpannya untuk digunakan di masa depan. Itu hal yang mahal untuk dilakukan. Tidak ada sizeof () operator di Jawa, dan bahkan jika ada, itu hanya akan menghitung biaya referensi ke objek lain dan ukuran primitif.

Salah satu cara Anda bisa melakukannya adalah dengan membuat serial hal itu ke File dan melihat ukuran file, seperti ini:

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

Tentu saja, ini mengasumsikan bahwa setiap objek berbeda dan tidak mengandung referensi non-transien untuk hal lain.

Strategi lain adalah mengambil setiap objek dan memeriksa anggotanya dengan refleksi dan menjumlahkan ukurannya (boolean & byte = 1 byte, pendek & char = 2 byte, dll.), Dengan menelusuri hierarki keanggotaan. Tapi itu membosankan dan mahal dan akhirnya melakukan hal yang sama dengan strategi serialisasi.


3
Saya akan membuat cerita bersambung ke byte [] menggunakan ByteArrayOutputStream. Ini akan jauh lebih cepat daripada menulisnya ke file.
ScArcher2

@ KorayTugay Menentukan ukuran byte dari suatu objek sudah merupakan operasi yang mahal. Menulis setiap objek ke disk untuk menentukan ukuran, hanya akan membuatnya merangkak ...
HammerNL

1
Format objek serial sepenuhnya berbeda dengan format objek dalam memori tumpukan. Terutama, deskriptor untuk kelas objek (dan semua superclasses berseri-seri) ditulis ke stream. Jadi menulis contoh sederhana java.lang.Integermenghasilkan sekitar 80 byte, di mana representasi heap biasanya 32 (tidak seperti representasi aliran objek, representasi heap tergantung pada ukuran pointer dan penyelarasan objek). Sebaliknya, nullreferensi serial membutuhkan satu byte, bukan empat atau delapan byte di memori tumpukan.
Holger
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.