Bagian Satu, Teoritis |
Bagian Dua, Praktis
Berdasarkan tweet dari Evgeny Mandrikov alias
godin :
Di dalamnya, ia bertanya-tanya berapa nilai maksimum yang dapat ditentukan dalam
enum
di Jawa. Setelah serangkaian percobaan dan penggunaan sihir hitam ConstantDynamic (
JEP 309 ), penulis pertanyaan muncul di nomor 8191.
Dalam serangkaian dua artikel, kami mencari batas teoritis jumlah elemen dalam pencacahan, mencoba untuk lebih dekat dengan mereka dalam praktik, dan mencari tahu bagaimana JEP 309 dapat membantu.
Pengintaian
Bab review di mana kita pertama kali melihat enumerasi dibongkar.Pertama, mari kita lihat apa yang diterjemahkan oleh enumerasi berikut:
public enum FizzBuzz { Fizz, Buzz, FizzBuzz; }
Setelah kompilasi dan pembongkaran:
javap -c -s -p -v FizzBuzz.class Classfile /dev/null/FizzBuzz.class Last modified 32 . 2019 .; size 903 bytes MD5 checksum add0af79de3e9a70a7bbf7d57dd0cfe7 Compiled from "FizzBuzz.java" public final class FizzBuzz extends java.lang.Enum<FizzBuzz> minor version: 0 major version: 58 flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM this_class: #2 // FizzBuzz super_class: #13 // java/lang/Enum interfaces: 0, fields: 4, methods: 4, attributes: 2 Constant pool: #1 = Fieldref #2.#3
Dalam daftar kami bertemu
- Satu bidang
public static final
untuk setiap nilai yang ditentukan dalam enumerasi - Bidang sintetis pribadi
$VALUES
, detail implementasi metode values()
- Implementasi metode
values()
dan valueOf()
- Konstruktor pribadi
- Blok inisialisasi statis, di mana sebenarnya hal paling menarik terjadi. Mari kita pertimbangkan lebih terinci.
Dalam bentuk kode java, yang terakhir terlihat seperti ini:
static { Fizz = new FizzBuzz("Fizz", 0); Buzz = new FizzBuzz("Buzz", 1); FizzBuzz = new FizzBuzz("FizzBuzz", 2); $VALUES = new FizzBuzz[] { Fizz, Buzz, FizzBuzz }; }
Pertama, contoh elemen enumerasi dibuat. Mesin virtual yang dibuat segera ditulis ke bidang
public static final
sesuai.
Kemudian sebuah array dibuat dan diisi dengan tautan ke instance dari semua elemen enumerasi. Tautan diambil dari bidang kelas yang kami inisialisasi dalam paragraf di atas. Array yang diisi disimpan di bidang
private static final
$VALUES
.
Setelah itu, daftar siap digunakan.
Bottleneck
Bab yang membosankan di mana kami mencari batasan jumlah elemen enumerasi.Anda dapat memulai pencarian Anda dengan bab
JLS ยง8.9.3 โAnggota Enumโ:
JLS 8.9.3 Anggota EnumAnggota enum tipe E semuanya adalah sebagai berikut:
...
* Untuk setiap konstanta enum c yang dinyatakan dalam badan deklarasi E, E miliki
bidang final statis publik yang dinyatakan secara implisit dari tipe E yang memiliki hal yang sama
beri nama c. Bidang ini memiliki penginisialisasi variabel yang instantiate E dan melewati apa pun
argumen c ke konstruktor yang dipilih untuk E. Field memiliki anotasi yang sama
as c (jika ada).
Bidang-bidang ini secara implisit dinyatakan dalam urutan yang sama dengan yang sesuai
enum konstanta, sebelum bidang statis apa pun secara eksplisit dinyatakan dalam badan
deklarasi E.
...
* Metode yang dinyatakan secara implisit berikut ini:
public static E[] values(); public static E valueOf(String name);
Jadi, setiap kelas enumerasi memiliki metode
values()
yang mengembalikan array dengan semua elemen yang dideklarasikan dalam enumerasi ini. Oleh karena itu, enumerasi bola dalam ruang hampa tidak dapat mengandung lebih dari elemen
Integer.MAX_VALUE + 1
.
Pindah. Enumerasi di Jawa direpresentasikan sebagai turunan dari kelas
java.lang.Enum
, dan karenanya mereka tunduk pada semua batasan yang melekat dalam kelas di JVM.
Mari kita lihat deskripsi tingkat tinggi dari struktur file kelas yang diberikan dalam
JVMS ยง4.1 "Struktur ClassFile":
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }
Seperti yang sudah kita ketahui dari JLS ยง8.9.3, bidang dengan nama yang sama dibuat untuk setiap elemen enumerasi di kelas yang dihasilkan. Jumlah bidang dalam kelas mendefinisikan 16-bit unsigned
fields_count
, yang membatasi kita untuk 65_535 bidang dalam satu file kelas atau 65_534 elemen enumerasi. Satu bidang dicadangkan untuk array
$VALUES
, klon yang mengembalikan metode
values()
. Ini tidak secara eksplisit dinyatakan dalam spesifikasi, tetapi tidak mungkin untuk datang dengan solusi yang lebih elegan.
Nama-nama bidang, metode, kelas, nilai konstan dan banyak lagi disimpan di kumpulan konstan.
Jika Anda tidak tahu apa-apa tentang struktur internal kolam konstan, saya sarankan membaca artikel kuno dari lany . Terlepas dari kenyataan bahwa sejak ditulis di kumpulan konstanta banyak hal baru dan menarik telah muncul, prinsip-prinsip dasar tetap tidak berubah.
Ukuran kumpulan konstanta kelas juga dibatasi oleh jumlah 65_535 elemen. Kumpulan konstanta dari kelas yang dibentuk dengan benar tidak pernah kosong. Minimal, akan ada nama untuk kelas ini.
Sebagai contoh, kumpulan konstanta dari kelas enumerasi kosong yang dikompilasi oleh javac dari OpenJDK 14-ea + 29 tanpa informasi debugging berisi 29 kejadian.
Oleh karena itu jumlah elemen 65_534 dalam satu enumerasi juga tidak dapat dicapai. Dalam kasus terbaik, kita dapat mengandalkan 65_505 atau angka yang dekat dengan ini.
Akord terakhir dalam pengantar yang berlarut-larut ini:
Nilai dapat
<clinit>
ke bidang
static final
hanya di blok inisialisasi statis, yang diwakili di tingkat file kelas dengan metode yang disebut
<clinit>
. Bytecode metode apa pun tidak dapat menempati lebih dari 65_535 byte. Nomor yang dikenal, bukan?
Satu instruksi penulisan statis
putstatic
membutuhkan 3 byte, yang memberi kita perkiraan kasar
65_535 / 3 = 21_845
. Faktanya, estimasi ini dilebih-lebihkan. Instruksi mengambil nilai untuk menulis ke bidang dari atas tumpukan, yang mana salah satu instruksi sebelumnya ditempatkan di sana. Dan instruksi ini juga memakan byte yang berharga. Tetapi bahkan jika Anda tidak memperhitungkannya, jumlah yang dihasilkan masih kurang dari 65_505.
Singkatnya:
- Format file kelas membatasi jumlah maksimum elemen enumerasi sekitar 65_505
- Mekanisme inisialisasi bidang akhir statis membatasi kita bahkan lebih. Secara teoritis - hingga 21_845 elemen maksimum, dalam praktiknya jumlah ini bahkan lebih sedikit
Dalam artikel terakhir dari seri ini, kami akan fokus pada optimasi yang tidak sehat dan pembuatan file kelas.