Tata Letak Memori boolean dan boolean di JVM

1. Gambaran keseluruhan

Dalam artikel ringkas ini, kita akan melihat apa jejak nilai boolean di JVM dalam keadaan yang berbeza.

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

2. Persediaan

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

 org.openjdk.jol jol-core 0.10 

3. Ukuran Objek

Sekiranya kami meminta JOL mencetak butiran VM dari segi Ukuran Objek:

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

Apabila rujukan yang dimampatkan diaktifkan (tingkah laku lalai), kita akan melihat outputnya:

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

Dalam beberapa baris pertama, kita dapat melihat beberapa maklumat umum mengenai VM. Selepas itu, kami belajar mengenai ukuran objek:

  • Rujukan Java menggunakan 4 bait, boolean s / byte s adalah 1 bait, char s / pendek s adalah 2 bait, int s / float s adalah 4 bait, dan akhirnya, panjang s / ganda s 8 bait
  • Jenis ini menggunakan jumlah memori yang sama walaupun kita menggunakannya sebagai elemen array

Jadi, dengan adanya rujukan yang dimampatkan, setiap nilai boolean mengambil 1 bait. Begitu juga, setiap boolean dalam boolean [] menggunakan 1 bait. Namun, padding penjajaran dan header objek dapat meningkatkan ruang yang digunakan oleh boolean dan boolean [] seperti yang akan kita lihat nanti.

3.1. Tiada Rujukan Mampat

Walaupun kita mematikan rujukan yang dimampatkan 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]

Sebaliknya, rujukan Java mengambil dua kali memori.

Oleh itu, walaupun pada awalnya kita jangkakan, booleans menggunakan 1 bait dan bukannya hanya 1 bit.

3.2. Mengoyak Perkataan

Dalam kebanyakan seni bina, tidak ada cara untuk mengakses satu bit secara atom. Walaupun kita mahu melakukannya, kita mungkin akan menulis bit yang berdekatan sambil mengemas kini yang lain.

Salah satu tujuan reka bentuk JVM adalah untuk mencegah fenomena ini, yang dikenali sebagai kata koyak . Maksudnya, dalam JVM, setiap medan dan elemen array harus berbeza; kemas kini ke satu medan atau elemen tidak boleh berinteraksi dengan pembacaan atau kemas kini bidang atau elemen lain.

Untuk merakam, masalah kebolehcantuman dan pemecahan kata adalah sebab utama mengapa boolean lebih daripada satu bit sahaja.

4. Penunjuk Objek Biasa (OOP)

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

class BooleanWrapper { private boolean value; }

Sekiranya kita memeriksa susun atur memori kelas ini menggunakan JOL:

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

Kemudian JOL akan mencetak susun atur 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 susun atur terdiri daripada:

  • 12 bait untuk tajuk, termasuk dua kata tanda dan satu kata klass . HotSpot JVM menggunakan kata tanda untuk menyimpan metadata GC, kod hash identiti dan maklumat penguncian. Juga, ia menggunakan kata klass untuk menyimpan metadata kelas seperti pemeriksaan jenis runtime
  • 1 bait untuk sebenar boolean nilai
  • 3 bait padding untuk tujuan penjajaran

Secara lalai, rujukan objek harus diselaraskan dengan 8 bait. Oleh itu, JVM menambah 3 bait menjadi 13 bait tajuk dan boolean untuk menjadikannya 16 bait.

Oleh itu, medan boolean mungkin menggunakan lebih banyak memori kerana penjajaran medan mereka.

4.1. Penjajaran Tersuai

Sekiranya kita mengubah nilai penjajaran menjadi 32 melalui -XX: ObjectAlignmentInBytes = 32, maka susun atur 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 menambah 19 bait padding untuk menjadikan ukuran objek menjadi gandaan 32.

5. Array OOP

Mari lihat bagaimana JVM menyusun susunan boolean dalam memori:

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

Ini akan mencetak susun atur contoh seperti 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 perbincangan yang lebih terperinci, sangat disarankan untuk melihat bahagian atas kod sumber JVM. Juga, Aleksey Shipilëv mempunyai artikel yang lebih mendalam di kawasan ini.

Seperti biasa, semua contoh boleh didapati di GitHub.