Java Constant Pool adalah topik yang tahan lama dan juga favorit pewawancara. Ada banyak jenis pertanyaan. Saya akan meringkasnya kali ini.
teori
Pertama, mari kita ungkapkan distribusi memori virtual JVM:
Penghitung program adalah pipa untuk JVM untuk menjalankan program, menyimpan beberapa instruksi lompatan. Ini terlalu mendalam dan saya tidak mengerti.
Tumpukan metode lokal adalah tumpukan yang digunakan oleh JVM untuk memanggil metode sistem operasi.
Tumpukan mesin virtual adalah tumpukan yang digunakan oleh JVM untuk menjalankan kode Java.
Metode area menyimpan beberapa konstanta, variabel statis, informasi kelas, dll., Yang dapat dipahami sebagai lokasi penyimpanan file kelas dalam memori.
Tumpukan mesin virtual adalah tumpukan yang digunakan oleh JVM untuk menjalankan kode Java.
Kolam konstan di Jawa sebenarnya dibagi menjadi dua bentuk: kolam konstan statis dan kolam runtime konstan .
Kumpulan konstan statis yang disebut adalah kumpulan konstan dalam file *.class. Kumpulan konstan dalam file kelas tidak hanya berisi literal string (angka), tetapi juga berisi informasi tentang kelas dan metode, menempati sebagian besar ruang file kelas.
Runtime Constant Pool adalah mesin virtual JVM memuat kumpulan konstan di file kelas ke dalam memori setelah menyelesaikan operasi pemuatan kelas dan menyimpannya di area metode . Kolam konstan yang sering kita sebut mengacu pada kolam runtime konstan di area metode.
Selanjutnya, kami mengutip beberapa contoh kumpulan konstan yang populer di internet dan kemudian menjelaskannya.
String s1 = "halo"; String s2 = "halo"; String s3 = "hel" + "lo"; String s4 = "hel" + string baru ("lo"); String s5 = string baru ("halo"); String s6 = s5.intern (); String s7 = "h"; String s8 = "ello"; String S9 = S7 + S8; System.out.println (S1 == S2); // truesystem.out.println (s1 == S3); // truesystem.out.println (s1 == S4); // falsesystem.out.println (s1 == S9); // falsesystem.out.println (s4 == S5); // falsesystem.out.println (s1 == s6); // BENARPertama -tama, di Java, operator == digunakan secara langsung, dan alamat referensi dari dua string dibandingkan, bukan isinya. Harap gunakan String.Equals () untuk membandingkan isinya.
S1 == S2 sangat mudah dimengerti. Ketika S1 dan S2 ditugaskan, mereka menggunakan string literal. Terus terang, mereka langsung menulis tali sampai mati. Selama kompilasi, literal ini akan ditempatkan langsung ke kumpulan file kelas yang konstan, sehingga mewujudkan penggunaan kembali. Setelah memuat kumpulan konstan saat runtime, S1 dan S2 titik ke alamat memori yang sama, sehingga mereka sama.
Ada lubang di S1 == S3. Meskipun S3 adalah string yang disambung secara dinamis, semua bagian yang terlibat dalam splicing diketahui literal. Selama periode kompilasi, splicing ini akan dioptimalkan, dan kompiler akan secara langsung membantu Anda menyambungkannya. Oleh karena itu, string s3 = "hel" + "lo"; dioptimalkan ke string s3 = "halo"; Dalam file kelas, jadi S1 == S3 benar.
S1 == S4 tentu saja tidak sama. Meskipun S4 juga disambung, string baru ("lo") bagian bukanlah literal yang diketahui, tetapi bagian yang tidak dapat diprediksi. Kompiler tidak akan mengoptimalkannya. Anda harus menunggu sampai lari untuk menentukan hasilnya. Dikombinasikan dengan teorema invarian string , Anda tahu di mana S4 dialokasikan, sehingga alamatnya harus berbeda. Gambar singkat untuk mengklarifikasi ide:
S1 == S9 tidak sama, dan alasannya serupa. Meskipun string literal yang digunakan oleh S7 dan S8 saat menetapkan nilai, saat menyambung ke S9, S7 dan S8 keduanya tidak dapat diprediksi. Bagaimanapun, kompiler adalah kompiler dan tidak dapat digunakan sebagai penerjemah, sehingga tidak dioptimalkan. Ketika dijalankan, string baru yang disambung ke S7 dan S8 tidak yakin di tumpukan dan tidak dapat sama dengan alamat S1 di kumpulan konstan area metode.
S4 == S5 tidak perlu lagi dijelaskan, itu jelas tidak sama, keduanya ada di tumpukan, tetapi alamatnya berbeda.
Kesetaraan S1 == S6 sepenuhnya dikaitkan dengan metode magang. S5 ada di tumpukan dan kontennya halo. Metode magang akan mencoba menambahkan string hello ke kumpulan konstan dan mengembalikan alamatnya di kumpulan konstan. Karena ada hello string di kumpulan konstan, metode magang secara langsung mengembalikan alamat; Sementara S1 sudah menunjuk ke kumpulan konstan selama periode kompilasi, jadi S1 dan S6 menunjuk ke alamat yang sama, yang sama.
Pada titik ini, kita dapat menarik tiga kesimpulan yang sangat penting:
Anda harus memperhatikan perilaku selama periode kompilasi untuk lebih memahami kumpulan konstan.
Konstanta di kumpulan konstan runtime pada dasarnya berasal dari kumpulan konstan di setiap file kelas.
Saat program berjalan, JVM tidak akan secara otomatis menambahkan konstanta ke kumpulan konstan kecuali secara manual menambahkan konstanta ke kumpulan konstan (seperti memanggil metode magang).
Di atas hanya melibatkan kolam konstanta string. Faktanya, ada kolam integer konstan, kolam renang konstan titik mengambang, dll., Tetapi mereka serupa, tetapi kumpulan tipe numerik yang konstan tidak dapat ditambahkan secara manual. Konstanta di kumpulan konstan ditentukan ketika program dimulai. Misalnya, kisaran konstan di kolam konstanta integer adalah: -128 ~ 127. Hanya angka dalam kisaran ini yang dapat digunakan untuk kumpulan konstan.
praktik
Setelah mengatakan begitu banyak teori, mari kita sentuh pada kolam konstan yang sebenarnya.
Seperti yang disebutkan sebelumnya, ada kumpulan konstan statis dalam file kelas. Kolam konstan ini dihasilkan oleh kompiler dan digunakan untuk menyimpan literal dalam file sumber Java (artikel ini hanya berfokus pada literal). Misalkan kita memiliki kode java berikut:
String s = "hai";
Untuk kenyamanan, sesederhana itu, itu benar! Setelah menyusun kode ke dalam file kelas, gunakan WinHex untuk membuka file kelas format biner. Seperti yang ditunjukkan pada gambar:
Mari kita jelaskan secara singkat struktur file kelas. 4 byte di awal adalah nomor ajaib dari file kelas, yang digunakan untuk mengidentifikasi ini sebagai file kelas. Terus terang, itu adalah header file, yaitu: ca fe ba be.
4 byte berikutnya adalah nomor versi Java, dan nomor versi di sini adalah 34, karena penulis dikompilasi dengan JDK8, dan nomor versi sesuai dengan level versi JDK. Versi yang lebih tinggi dapat kompatibel dengan versi yang lebih rendah, tetapi versi yang lebih rendah tidak dapat menjalankan versi yang lebih tinggi. Jadi, jika suatu hari pembaca ingin tahu apa versi JDK, file kelas orang lain dikompilasi, Anda dapat melihat 4 byte ini.
Berikutnya adalah pintu masuk kolam yang konstan. Jumlah konstanta kolam konstan diidentifikasi oleh 2 byte di pintu masuk. Dalam contoh ini, nilainya adalah 00 1a. Ini diterjemahkan ke dalam desimal dan 26, yang berarti ada 25 konstanta. Konstanta ke -0 adalah nilai khusus, jadi hanya ada 25 konstanta.
Pool konstan menyimpan berbagai jenis konstanta. Mereka semua memiliki tipe sendiri dan spesifikasi penyimpanan mereka sendiri. Artikel ini hanya berfokus pada konstanta string. Konstanta string mulai dengan 01 (1 byte), dan kemudian merekam panjang string dengan 2 byte, dan kemudian konten aktual dari string. Dalam hal ini, ini adalah: 01 00 02 68 69.
Selanjutnya, mari kita bicara tentang runtime constant pool. Karena kumpulan konstanta runtime berada di area metode, kami dapat mengatur ukuran area metode melalui parameter JVM: -xx: permsize, -xx: maxpermsize, sehingga secara tidak langsung membatasi ukuran kolam konstan.
Misalkan parameter startup JVM adalah: -xx: Permsize = 2m -xx: maxpermsize = 2m, dan kemudian jalankan kode berikut:
// Simpan referensi untuk mencegah daftar koleksi sampah otomatis <string> list = new ArrayList <String> (); int i = 0; while (true) {// secara manual menambahkan list.add konstan (string.valueof (i ++). Intern ());}Program ini akan segera melempar: Pengecualian di Thread "Main" java.lang.outofmemoryError: Permgen Space Exception. Ruang Permgen adalah area metode, yang cukup untuk menunjukkan bahwa kumpulan konstan berada di area metode.
Di JDK8, area metode telah dihapus dan area Metaspace diganti. Oleh karena itu, kita perlu menggunakan parameter JVM baru: -xx: maxmetaspacesize = 2m, dan masih menjalankan kode di atas, melempar: java.lang.outofmemoryError: Metaspace Exception. Demikian pula, dijelaskan bahwa kumpulan konstan runtime dibagi menjadi area metaspace. Untuk pengetahuan khusus tentang area Metaspace, silakan cari sendiri.
Semua kode dalam artikel ini telah diuji dan diteruskan di bawah JDK7 dan JDK8. Versi JDK lainnya mungkin memiliki sedikit perbedaan. Silakan jelajahi sendiri.