boolean dan boolean Memory Layout di JVM

1. Ikhtisar

Dalam artikel singkat ini, kita akan melihat apa itu footprint dari nilai boolean di JVM dalam keadaan yang berbeda.

Pertama, kami akan memeriksa JVM untuk melihat ukuran objek. Kemudian, kita akan memahami alasan di balik ukuran tersebut.

2. Penyiapan

Untuk memeriksa tata letak memori objek di JVM, kita akan menggunakan Java Object Layout (JOL) secara ekstensif. Oleh karena itu, kita perlu menambahkan dependensi jol-core :

 org.openjdk.jol jol-core 0.10 

3. Ukuran Objek

Jika kami meminta JOL untuk mencetak detail VM dalam hal Ukuran Objek:

System.out.println(VM.current().details());

Saat referensi terkompresi diaktifkan (perilaku default), kita akan melihat hasilnya:

# 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]

Di beberapa baris pertama, kita dapat melihat beberapa informasi umum tentang VM. Setelah itu, kita belajar tentang ukuran objek:

  • Referensi Java menggunakan 4 byte, boolean s / byte s 1 byte, char s / short s 2 byte, int s / float adalah 4 byte, dan terakhir, panjang s / double s 8 byte
  • Tipe-tipe ini mengkonsumsi jumlah memori yang sama bahkan ketika kita menggunakannya sebagai elemen array

Jadi, dengan adanya referensi yang dikompresi, setiap nilai boolean membutuhkan 1 byte. Demikian pula, setiap boolean dalam boolean [] mengkonsumsi 1 byte. Namun, paddings dan header objek dapat meningkatkan ruang yang digunakan oleh boolean dan boolean [] seperti yang akan kita lihat nanti.

3.1. Tidak Ada Referensi Terkompresi

Meskipun kami menonaktifkan referensi terkompresi melalui -XX: -UseCompressedOops , ukuran boolean tidak akan berubah sama sekali :

# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

Di sisi lain, referensi Java menghabiskan memori dua kali lipat.

Jadi terlepas dari apa yang mungkin kita harapkan pada awalnya, boolean mengonsumsi 1 byte, bukan hanya 1 bit.

3.2. Merobek Kata

Dalam kebanyakan arsitektur, tidak ada cara untuk mengakses satu bit pun secara atomik. Bahkan jika kami ingin melakukannya, kami mungkin akan menulis ke bit yang berdekatan sambil memperbarui bit lainnya.

Salah satu tujuan desain JVM adalah untuk mencegah fenomena ini, yang dikenal sebagai word tearing . Artinya, di JVM, setiap bidang dan elemen array harus berbeda; pembaruan ke satu bidang atau elemen tidak boleh berinteraksi dengan pembacaan atau pembaruan bidang atau elemen lainnya.

Singkatnya, masalah pengalamatan dan robekan kata adalah alasan utama mengapa boolean lebih dari satu bit.

4. Ordinary Object Pointers (OOPs)

Sekarang kita tahu boolean adalah 1 byte, mari pertimbangkan kelas sederhana ini:

class BooleanWrapper { private boolean value; }

Jika kita memeriksa tata letak memori kelas ini menggunakan JOL:

System.out.println(ClassLayout.parseClass(BooleanWrapper.class).toPrintable());

Kemudian JOL akan mencetak tata letak memori:

 OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 1 boolean BooleanWrapper.value N/A 13 3 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

The BooleanWrapper tata letak terdiri dari:

  • 12 byte untuk header, termasuk dua kata mark dan satu kata klass . HotSpot JVM menggunakan kata mark untuk menyimpan metadata GC, kode hash identitas, dan informasi penguncian. Selain itu, ia menggunakan kata klass untuk menyimpan metadata kelas seperti pemeriksaan jenis runtime
  • 1 byte untuk aktual boolean nilai
  • 3 byte padding untuk tujuan penyelarasan

Secara default, referensi objek harus disejajarkan dengan 8 byte. Oleh karena itu, JVM menambahkan 3 byte ke 13 byte header dan boolean menjadi 16 byte.

Oleh karena itu, bidang boolean dapat menggunakan lebih banyak memori karena perataan bidangnya.

4.1. Perataan Kustom

Jika kita mengubah nilai perataan menjadi 32 melalui -XX: ObjectAlignmentInBytes = 32, maka tata letak kelas yang sama berubah menjadi:

OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 1 boolean BooleanWrapper.value N/A 13 19 (loss due to the next object alignment) Instance size: 32 bytes Space losses: 0 bytes internal + 19 bytes external = 19 bytes total

Seperti yang ditunjukkan di atas, JVM menambahkan 19 byte padding untuk membuat ukuran objek kelipatan 32.

5. Array OOP

Mari kita lihat bagaimana JVM meletakkan array boolean dalam memori:

boolean[] value = new boolean[3]; System.out.println(ClassLayout.parseInstance(value).toPrintable());

Ini akan mencetak tata letak instance sebagai berikut:

OFFSET SIZE TYPE DESCRIPTION 0 4 (object header) # mark word 4 4 (object header) # mark word 8 4 (object header) # klass word 12 4 (object header) # array length 16 3 boolean [Z. # [Z means boolean array 19 5 (loss due to the next object alignment)

In addition to two mark words and one klass word, array pointers contain an extra 4 bytes to store their lengths.

Since our array has three elements, the size of the array elements is 3 bytes. However, these 3 bytes will be padded by 5 field alignment bytes to ensure proper alignment.

Although each boolean element in an array is just 1 byte, the whole array consumes much more memory. In other words, we should consider the header and padding overhead while computing the array size.

6. Conclusion

In this quick tutorial, we saw that boolean fields are consuming 1 byte. Also, we learned that we should consider the header and padding overheads in object sizes.

Untuk pembahasan yang lebih mendetail, sangat disarankan untuk memeriksa bagian ups dari kode sumber JVM. Selain itu, Aleksey Shipilëv memiliki artikel yang jauh lebih mendalam di bidang ini.

Seperti biasa, semua contoh tersedia di GitHub.