Berapa konsumsi memori dari suatu objek di Jawa?

216

Apakah ruang memori yang dikonsumsi oleh satu objek dengan 100 atribut sama dengan 100 objek, masing-masing dengan satu atribut?

Berapa banyak memori yang dialokasikan untuk suatu objek?
Berapa banyak ruang tambahan yang digunakan saat menambahkan atribut?

keparo
sumber

Jawaban:

180

Mindprod menunjukkan bahwa ini bukan pertanyaan langsung untuk dijawab:

JVM bebas untuk menyimpan data dengan cara apa pun yang diinginkan secara internal, endian besar atau kecil, dengan sejumlah padding atau overhead, meskipun primitif harus berperilaku seolah-olah mereka memiliki ukuran resmi.
Sebagai contoh, JVM atau kompiler asli mungkin memutuskan untuk menyimpan potongan boolean[]64-bit panjang seperti a BitSet. Tidak harus memberi tahu Anda, asalkan program memberikan jawaban yang sama.

  • Ini mungkin mengalokasikan beberapa Objek sementara di stack.
  • Ini dapat mengoptimalkan beberapa variabel atau pemanggilan metode yang sama sekali tidak ada dan menggantinya dengan konstanta.
  • Ini mungkin versi metode atau loop, yaitu mengkompilasi dua versi metode, masing-masing dioptimalkan untuk situasi tertentu, lalu memutuskan di depan mana yang akan dipanggil.

Maka tentu saja perangkat keras dan OS memiliki cache multilayer, pada chip-cache, cache SRAM, cache DRAM, RAM yang biasa bekerja dan menyimpan cadangan di disk. Data Anda dapat digandakan di setiap level cache. Semua kompleksitas ini berarti Anda hanya dapat memperkirakan konsumsi RAM secara kasar.

Metode pengukuran

Anda bisa menggunakan Instrumentation.getObjectSize()untuk mendapatkan perkiraan penyimpanan yang dikonsumsi oleh suatu objek.

Untuk memvisualisasikan sebenarnya tata letak objek, jejak, dan referensi, Anda dapat menggunakan JOL (Jawa Obyek Layout) alat .

Header objek dan referensi Objek

Dalam JDK 64-bit modern, sebuah objek memiliki header 12-byte, diisi hingga kelipatan 8 byte, sehingga ukuran objek minimum adalah 16 byte. Untuk JVM 32-bit, overhead adalah 8 byte, diisi hingga kelipatan 4 byte. (Dari jawaban Dmitry Spikhalskiy ini , jawaban Jayen ini , dan JavaWorld .)

Biasanya, referensi adalah 4 byte pada platform 32bit atau pada platform 64bit hingga -Xmx32G; dan 8 byte di atas 32Gb ( -Xmx32G). (Lihat referensi objek terkompresi .)

Akibatnya, JVM 64-bit biasanya membutuhkan 30-50% lebih banyak ruang tumpukan. ( Haruskah saya menggunakan JVM 32 atau 64-bit?, 2012, JDK 1.7)

Jenis kotak, array, dan string

Pembungkus kotak memiliki overhead dibandingkan dengan tipe primitif (dari JavaWorld ):

  • Integer: Hasil 16-byte sedikit lebih buruk daripada yang saya harapkan karena intnilai dapat masuk ke dalam hanya 4 byte tambahan. Menggunakan Integersaya biaya overhead memori 300 persen dibandingkan ketika saya bisa menyimpan nilai sebagai tipe primitif

  • Long: 16 byte juga: Jelas, ukuran objek aktual pada heap tunduk pada penyelarasan memori tingkat rendah yang dilakukan oleh implementasi JVM tertentu untuk jenis CPU tertentu. Sepertinya Long8 byte overhead objek, ditambah 8 byte lebih untuk nilai panjang sebenarnya. Sebaliknya, Integermemiliki lubang 4-byte yang tidak digunakan, kemungkinan besar karena JVM saya menggunakan penyelarasan objek gaya pada batas kata 8-byte.

Wadah lain juga mahal:

  • Array multidimensi : ia menawarkan kejutan lain.
    Pengembang biasanya menggunakan konstruksi seperti int[dim1][dim2]dalam komputasi numerik dan ilmiah.

    Dalam int[dim1][dim2]contoh array, setiap int[dim2]array bersarang adalah Objectdalam dirinya sendiri. Masing-masing menambahkan overhead array 16-byte yang biasa. Ketika saya tidak membutuhkan array segitiga atau compang-camping, itu mewakili overhead murni. Dampaknya tumbuh ketika dimensi array sangat berbeda.

    Sebagai contoh, sebuah int[128][2]instance membutuhkan 3.600 byte. Dibandingkan dengan 1.040 byte yang int[256]digunakan instance (yang memiliki kapasitas yang sama), 3.600 byte mewakili overhead 246 persen. Dalam kasus ekstrim byte[256][1], faktor overhead hampir 19! Bandingkan dengan situasi C / C ++ di mana sintaksis yang sama tidak menambahkan overhead penyimpanan.

  • String: Stringpertumbuhan memori a melacak pertumbuhan char array internal. Namun, Stringkelas menambahkan 24 byte overhead.

    Untuk nonempty Stringdari ukuran 10 karakter atau kurang, biaya overhead tambahan relatif terhadap payload berguna (2 byte untuk setiap karakter ditambah 4 byte untuk panjang), berkisar 100 hingga 400 persen.

Penjajaran

Pertimbangkan contoh objek ini :

class X {                      // 8 bytes for reference to the class definition
   int a;                      // 4 bytes
   byte b;                     // 1 byte
   Integer c = new Integer();  // 4 bytes for a reference
}

Jumlah yang naif akan menyarankan bahwa turunan Xakan menggunakan 17 byte. Namun, karena perataan (juga disebut padding), JVM mengalokasikan memori dalam kelipatan 8 byte, jadi alih-alih 17 byte itu akan mengalokasikan 24 byte.

VONC
sumber
int [128] [6]: 128 larik 6 int - 768 int total, 3072 byte data + 2064 byte Overhead objek = total 5166 byte. int [256]: Total 256 int - oleh karena itu tidak dapat dibandingkan. int [768]: 3072 byte data + 16 bye overhead - sekitar 3/5 ruang dari array 2D - tidak cukup 246% overhead!
JeeBee
Ah, artikel asli menggunakan int [128] [2] bukan int [128] [6] - bertanya-tanya bagaimana itu bisa berubah. Juga menunjukkan bahwa contoh ekstrim dapat menceritakan kisah yang berbeda.
JeeBee
2
Overhead adalah 16 byte dalam 64bit JVM.
Tim Cooper
3
@AlexWien: Beberapa skema pengumpulan sampah dapat mengenakan ukuran objek minimum yang terpisah dari padding. Selama pengumpulan sampah, setelah suatu objek disalin dari lokasi lama ke yang baru, lokasi lama mungkin tidak perlu lagi menyimpan data untuk objek tersebut, tetapi ia harus memiliki referensi ke lokasi baru; mungkin juga perlu menyimpan referensi ke lokasi lama objek di mana referensi pertama ditemukan dan offset referensi itu di dalam objek lama [karena objek lama mungkin masih mengandung referensi yang belum diproses].
supercat
2
@AlexWien: Menggunakan memori di lokasi lama suatu objek untuk menyimpan informasi pembukuan pengumpul sampah menghindari kebutuhan untuk mengalokasikan memori lain untuk tujuan itu, tetapi dapat memaksakan ukuran objek minimum yang lebih besar dari yang seharusnya diperlukan. Saya pikir setidaknya satu versi .NET pengumpulan sampah menggunakan pendekatan itu; tentu akan mungkin bagi beberapa pengumpul sampah Jawa untuk melakukannya juga.
supercat
34

Tergantung pada arsitektur / jdk. Untuk arsitektur JDK dan 64bit modern, sebuah objek memiliki header 12-byte dan padding dengan 8 byte - jadi ukuran objek minimum adalah 16 byte. Anda bisa menggunakan alat yang disebut Java Object Layout untuk menentukan ukuran dan mendapatkan detail tentang tata letak objek dan struktur internal dari setiap entitas atau menebak informasi ini dengan referensi kelas. Contoh output untuk Integer di lingkungan saya:

Running 64-bit HotSpot VM.
Using compressed oop with 3-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]

java.lang.Integer object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    12       (object header)                N/A
     12     4   int Integer.value                  N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

Jadi, untuk Integer, ukuran instance adalah 16 byte, karena 4-byte int dipadatkan tepat setelah header dan sebelum batas padding.

Contoh kode:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
    System.out.println(VMSupport.vmDetails());
    System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

Jika Anda menggunakan pakar, untuk mendapatkan JOL:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>
Dmitry Spikhalskiy
sumber
28

Setiap objek memiliki overhead tertentu untuk monitor terkait dan mengetik informasi, serta bidang itu sendiri. Selain itu, bidang dapat ditata cukup banyak namun JVM cocok (saya percaya) - tetapi seperti yang ditunjukkan dalam jawaban lain , setidaknya beberapa JVM akan dikemas cukup ketat. Pertimbangkan kelas seperti ini:

public class SingleByte
{
    private byte b;
}

vs.

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

Pada JVM 32-bit, saya berharap 100 instance SingleByteuntuk mengambil 1200 byte (8 byte overhead + 4 byte untuk bidang karena padding / alignment). Saya berharap satu contoh OneHundredBytesuntuk mengambil 108 byte - overhead, dan kemudian 100 byte, dikemas. Meskipun dapat bervariasi menurut JVM - satu implementasi mungkin memutuskan untuk tidak mengemas bidang OneHundredBytes, sehingga membutuhkan waktu 408 byte (= 8 byte overhead + 4 * 100 byte aligned / padded byte). Pada JVM 64 bit, overhead mungkin lebih besar juga (tidak yakin).

EDIT: Lihat komentar di bawah ini; rupanya HotSpot pad ke batas 8 byte, bukan 32, sehingga setiap instance SingleByteakan mengambil 16 byte.

Apa pun itu, "objek besar tunggal" setidaknya akan seefisien beberapa objek kecil - untuk kasus sederhana seperti ini.

Jon Skeet
sumber
9
Sebenarnya, satu instance dari SingleByte akan mengambil 16 byte pada Sun JVM, yaitu 8 byte overhead, 4 byte untuk field, dan kemudian 4 byte untuk objek padding, karena kompiler HotSpot
membulatkan
6

Total memori yang digunakan / bebas dari suatu program dapat diperoleh melalui program

java.lang.Runtime.getRuntime();

Runtime memiliki beberapa metode yang berkaitan dengan memori. Contoh pengkodean berikut menunjukkan penggunaannya.

package test;

 import java.util.ArrayList;
 import java.util.List;

 public class PerformanceTest {
     private static final long MEGABYTE = 1024L * 1024L;

     public static long bytesToMegabytes(long bytes) {
         return bytes / MEGABYTE;
     }

     public static void main(String[] args) {
         // I assume you will know how to create a object Person yourself...
         List < Person > list = new ArrayList < Person > ();
         for (int i = 0; i <= 100000; i++) {
             list.add(new Person("Jim", "Knopf"));
         }
         // Get the Java runtime
         Runtime runtime = Runtime.getRuntime();
         // Run the garbage collector
         runtime.gc();
         // Calculate the used memory
         long memory = runtime.totalMemory() - runtime.freeMemory();
         System.out.println("Used memory is bytes: " + memory);
         System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
     }
 }
nazar_art
sumber
6

Tampaknya setiap objek memiliki overhead 16 byte pada sistem 32-bit (dan 24-byte pada sistem 64-bit).

http://algs4.cs.princeton.edu/14analysis/ adalah sumber informasi yang bagus. Salah satu contoh di antara banyak yang bagus adalah sebagai berikut.

masukkan deskripsi gambar di sini

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efisien-java-tutorial.pdf juga sangat informatif, misalnya:

masukkan deskripsi gambar di sini

Arun
sumber
"Tampaknya setiap objek memiliki overhead 16 byte pada sistem 32-bit (dan 24-byte pada sistem 64-bit)." Itu tidak benar, setidaknya untuk JDK saat ini. Lihatlah jawaban saya untuk contoh Integer. Overhead objek setidaknya 12 byte untuk header untuk sistem 64-bit dan JDK modern. Dapat lebih karena padding, tergantung pada tata letak aktual bidang dalam objek.
Dmitry Spikhalskiy
Tautan kedua ke tutorial Java hemat memori sepertinya sudah mati - saya mendapatkan "Forbidden".
tsleyson
6

Apakah ruang memori yang dikonsumsi oleh satu objek dengan 100 atribut sama dengan 100 objek, masing-masing dengan satu atribut?

Tidak.

Berapa banyak memori yang dialokasikan untuk suatu objek?

  • Overhead adalah 8 byte pada 32-bit, 12 byte pada 64-bit; dan kemudian dibulatkan menjadi kelipatan 4 byte (32-bit) atau 8 byte (64-bit).

Berapa banyak ruang tambahan yang digunakan saat menambahkan atribut?

  • Atribut berkisar dari 1 byte (byte) hingga 8 byte (panjang / ganda), tetapi rujukannya adalah 4 byte atau 8 byte tergantung tidak pada apakah 32bit atau 64bit, melainkan apakah -Xmx adalah <32Gb atau> = 32Gb: khas 64 -bit JVM's memiliki optimasi yang disebut "-UseCompressedOops" yang memampatkan referensi hingga 4 byte jika heapnya di bawah 32GB.
Jayen
sumber
1
char adalah 16bit, bukan 8bit.
comonad
kamu benar. seseorang sepertinya telah mengedit jawaban asli saya
Jayen
5

Tidak, mendaftarkan objek juga membutuhkan sedikit memori. 100 objek dengan 1 atribut akan memakan lebih banyak memori.

Mendelt
sumber
4

Pertanyaannya akan sangat luas.

Itu tergantung pada variabel kelas atau Anda dapat memanggil sebagai menyatakan penggunaan memori di java.

Ini juga memiliki beberapa persyaratan memori tambahan untuk header dan referensi.

Memori tumpukan yang digunakan oleh objek Java termasuk

  • memori untuk bidang primitif, sesuai dengan ukurannya (lihat di bawah untuk Ukuran tipe primitif);

  • memori untuk bidang referensi (masing-masing 4 byte);

  • header objek, terdiri dari beberapa byte informasi "housekeeping";

Objek di java juga memerlukan beberapa informasi "housekeeping", seperti merekam kelas objek, ID dan bendera status seperti apakah objek saat ini dapat dijangkau, saat ini dikunci sinkronisasi dll.

Ukuran header objek Java bervariasi pada 32 dan 64 bit jvm.

Meskipun ini adalah memori utama, konsumen juga memerlukan bidang tambahan terkadang seperti untuk menyelaraskan kode, dll

Ukuran tipe primitif

boolean & byte - 1

char & short - 2

int & float - 4

panjang & ganda - 8

Nikhil Agrawal
sumber
Pembaca juga dapat menemukan makalah ini sangat mencerahkan: cs.virginia.edu/kim/publicity/pldi09tutorials/…
quellish
2

Jika berguna bagi siapa pun, Anda dapat mengunduh dari agen kecil Java dari situs web saya untuk menanyakan penggunaan memori suatu objek . Ini akan membiarkan Anda meminta penggunaan memori "dalam" juga.

Neil Coffey
sumber
Ini bekerja sangat baik untuk mendapatkan perkiraan kasar berapa banyak memori yang (String, Integer)digunakan Cache Guava, per-elemen. Terima kasih!
Steve K
1

tidak, 100 benda kecil membutuhkan lebih banyak informasi (memori) daripada satu besar.

Burkhard
sumber
0

Aturan tentang berapa banyak memori yang dikonsumsi tergantung pada implementasi JVM dan arsitektur CPU (misalnya 32 bit versus 64 bit).

Untuk aturan terperinci untuk SUN JVM, periksa blog lama saya

Salam, Markus

Kohlerm
sumber
Saya cukup yakin Sun Java 1.6 64bit, perlu 12 byte untuk objek polos + 4 padding = 16; bidang Object + one integer = 12 + 4 = 16
AlexWien
Apakah Anda mematikan blog Anda?
Johan Boulé
Tidak juga, Tidak yakin blog SAP entah bagaimana pindah. Sebagian besar dapat ditemukan di sini kohlerm.blogspot.com
kohlerm