Jawaban:
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 potonganboolean[]
64-bit panjang seperti aBitSet
. 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.
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 .
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)
Pembungkus kotak memiliki overhead dibandingkan dengan tipe primitif (dari JavaWorld ):
Integer
: Hasil 16-byte sedikit lebih buruk daripada yang saya harapkan karenaint
nilai dapat masuk ke dalam hanya 4 byte tambahan. MenggunakanInteger
saya 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. SepertinyaLong
8 byte overhead objek, ditambah 8 byte lebih untuk nilai panjang sebenarnya. Sebaliknya,Integer
memiliki 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 sepertiint[dim1][dim2]
dalam komputasi numerik dan ilmiah.Dalam
int[dim1][dim2]
contoh array, setiapint[dim2]
array bersarang adalahObject
dalam 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 yangint[256]
digunakan instance (yang memiliki kapasitas yang sama), 3.600 byte mewakili overhead 246 persen. Dalam kasus ekstrimbyte[256][1]
, faktor overhead hampir 19! Bandingkan dengan situasi C / C ++ di mana sintaksis yang sama tidak menambahkan overhead penyimpanan.
String
:String
pertumbuhan memori a melacak pertumbuhan char array internal. Namun,String
kelas menambahkan 24 byte overhead.Untuk nonempty
String
dari 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.
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 X
akan 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.
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>
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 SingleByte
untuk mengambil 1200 byte (8 byte overhead + 4 byte untuk bidang karena padding / alignment). Saya berharap satu contoh OneHundredBytes
untuk 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 SingleByte
akan mengambil 16 byte.
Apa pun itu, "objek besar tunggal" setidaknya akan seefisien beberapa objek kecil - untuk kasus sederhana seperti ini.
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));
}
}
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.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efisien-java-tutorial.pdf juga sangat informatif, misalnya:
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?
Berapa banyak ruang tambahan yang digunakan saat menambahkan atribut?
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
Saya mendapatkan hasil yang sangat baik dari pendekatan java.lang.instrument.Instrumentation yang disebutkan dalam jawaban lain. Untuk contoh penggunaannya yang baik, lihat entri, Penghitung Memori Instrumentasi dari JavaSpecialists 'Newsletter dan perpustakaan java.sizeOf di SourceForge.
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.
(String, Integer)
digunakan Cache Guava, per-elemen. Terima kasih!
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