Ada dua bentuk pembuatan objek string di Java, satu adalah bentuk literal, seperti String str = "droid";, dan yang lainnya adalah menggunakan metode standar baru untuk membuat objek, seperti String str = new String( " droid");, kedua metode ini sering digunakan saat menulis kode, terutama metode literal. Namun, sebenarnya terdapat beberapa perbedaan dalam performa dan penggunaan memori antara kedua implementasi ini. Semua ini disebabkan oleh fakta bahwa untuk mengurangi pembuatan objek string yang berulang-ulang, JVM menyimpan memori khusus. Memori ini disebut kumpulan konstanta string atau kumpulan literal string.
Prinsip kerja
Ketika objek string dibuat dalam bentuk literal dalam kode, JVM akan memeriksa literalnya terlebih dahulu. Jika ada referensi ke objek string dengan konten yang sama di kumpulan konstanta string, referensi akan dikembalikan, jika tidak string baru akan dibuat. Objek dibuat, referensi ini dimasukkan ke dalam kumpulan konstanta string, dan referensi dikembalikan.
Berikan sebuah contoh
Bentuk penciptaan literal
Copy kode kodenya sebagai berikut:
String str1 = "droid";
JVM mendeteksi literal ini. Di sini kami berpikir bahwa tidak ada objek yang isinya droid. JVM tidak dapat menemukan keberadaan objek string dengan konten droid melalui kumpulan konstanta string, kemudian akan membuat objek string, kemudian memasukkan referensi objek yang baru dibuat ke dalam kumpulan konstanta string, dan mengembalikan referensi ke variabel str1 .
Jika ada potongan kode seperti ini selanjutnya
Copy kode kodenya sebagai berikut:
String str2 = "droid";
Demikian pula, JVM masih perlu mendeteksi literal ini. JVM mencari kumpulan konstanta string dan menemukan bahwa objek string dengan konten "droid" ada, sehingga mengembalikan referensi objek string yang ada ke variabel str2. Perhatikan bahwa objek string baru tidak dibuat ulang di sini.
Untuk memverifikasi apakah str1 dan str2 menunjuk ke objek yang sama, kita dapat menggunakan kode ini
Copy kode kodenya sebagai berikut:
Sistem.keluar.println(str1 == str2);
Hasilnya benar.
Buat menggunakan yang baru
Copy kode kodenya sebagai berikut:
String str3 = String baru("droid");
Saat kita menggunakan new untuk membuat objek string, objek string baru akan dibuat terlepas dari apakah ada referensi ke objek dengan konten yang sama di kumpulan konstanta string. Jadi kami menggunakan kode berikut untuk mengujinya,
Copy kode kodenya sebagai berikut:
String str3 = String baru("droid");
Sistem.keluar.println(str1 == str3);
Hasilnya salah seperti yang kita duga, yang menunjukkan bahwa kedua variabel tersebut menunjuk pada objek yang berbeda.
magang
Untuk objek string yang dibuat menggunakan new di atas, jika Anda ingin menambahkan referensi objek ini ke kumpulan konstanta string, Anda dapat menggunakan metode intern.
Setelah memanggil magang, periksa dulu apakah ada referensi ke objek di kumpulan konstanta string. Jika ada, kembalikan referensi ke variabel. Jika tidak, tambahkan referensi dan kembalikan ke variabel.
Copy kode kodenya sebagai berikut:
String str4 = str3.magang();
Sistem.keluar.println(str4 == str1);
Hasil keluarannya benar.
Pertanyaan sulit
Prasyarat?
Prasyarat untuk implementasi kumpulan konstanta string adalah bahwa objek String di Java tidak dapat diubah, yang dapat dengan aman memastikan bahwa beberapa variabel berbagi objek yang sama. Jika objek String di Java bisa berubah dan operasi referensi mengubah nilai objek, variabel lain juga akan terpengaruh.
referensi atau objek
Masalah yang paling umum adalah apakah referensi atau objek disimpan dalam kumpulan konstanta string. Kumpulan konstanta string menyimpan referensi objek, bukan objek. Di Java, objek dibuat di memori heap.
Perbarui verifikasi, banyak komentar yang diterima juga membahas masalah ini, saya cukup memverifikasinya. Lingkungan verifikasi:
Copy kode kodenya sebagai berikut:
22:18:54-androidyue~/Video$ cat /etc/os-release
NAMA=Fedora
VERSION="17 (Keajaiban Gemuk)"
ID=fedora
VERSION_ID=17
PRETTY_NAME="Fedora 17 (Keajaiban Gemuk)"
ANSI_COLOR="0;34"
CPE_NAME="cpe:/o:fedoraproject:fedora:17"
22:19:04-androidyue~/Video$ java -version
versi java "1.7.0_25"
Lingkungan Waktu Proses OpenJDK (fedora-2.3.12.1.fc17-x86_64)
OpenJDK 64-Bit Server VM (build 23.7-b01, mode campuran)
Ide verifikasi: Program Java berikut membaca file video berukuran 82M dan melakukan operasi magang dalam bentuk string.
Copy kode kodenya sebagai berikut:
22:01:17-androidyue~/Video$ ll -lh |. grep kenapa_to_learn.mp4
-rw-rw-r--.1 androidyue androidyue 82M 20 Okt 2013 kenapa_to_learn.mp4
Kode verifikasi
Copy kode kodenya sebagai berikut:
impor java.io.BufferedReader;
impor java.io.FileNotFoundException;
impor java.io.FileReader;
impor java.io.IOException;
kelas publik TesUtama {
Konten file String statis pribadi;
public static void main(String[] args) {
fileContent = readFileToString(args[0]);
if (null != isi file) {
fileContent = fileContent.intern();
System.out.println("Bukan Null");
}
}
String statis pribadi readFileToString(String file) {
Pembaca BufferedReader = null;
mencoba {
pembaca = BufferedReader baru(FileReader baru(file));
Penggemar StringBuffer = StringBuffer baru();
garis senar;
while ((baris = pembaca.readLine()) != null) {
buff.append(baris);
}
kembalikan buff.toString();
} tangkapan (FileNotFoundException e) {
e.printStackTrace();
} tangkapan (IOException e) {
e.printStackTrace();
} Akhirnya {
if (null != pembaca) {
mencoba {
pembaca.close();
} tangkapan (IOException e) {
e.printStackTrace();
}
}
}
kembalikan nol;
}
}
Karena kumpulan konstanta string ada dalam pembuatan permanen di memori heap, ini berlaku sebelum Java8. Kami memverifikasi hal ini dengan menetapkan pembangkitan permanen ke nilai yang sangat kecil. Jika objek string ada di kumpulan konstanta string, maka kesalahan ruang permgen java.lang.OutOfMemoryError pasti akan terjadi.
Copy kode kodenya sebagai berikut:
java -XX:PermSize=6m TestMain ~/Videos/why_to_learn.mp4
Menjalankan program bukti tidak menghilangkan OOM. Faktanya, ini tidak dapat membuktikan dengan baik apakah objek atau referensi disimpan.
Tapi ini setidaknya membuktikan bahwa objek konten aktual char[] dari string tidak disimpan dalam kumpulan konstanta string. Dalam hal ini, sebenarnya tidak terlalu penting apakah kumpulan konstanta string menyimpan objek string atau referensi ke objek string. Namun secara pribadi saya masih lebih suka menyimpannya sebagai referensi.
Keuntungan dan Kerugian
Keuntungan dari kumpulan konstanta string adalah mengurangi pembuatan string dengan konten yang sama dan menghemat ruang memori.
Jika kita bersikeras membicarakan kerugiannya, waktu komputasi CPU dikorbankan demi mendapatkan ruang. Waktu perhitungan CPU terutama digunakan untuk mengetahui apakah ada referensi ke objek dengan konten yang sama di kumpulan konstanta string. Namun, implementasi internalnya adalah HashTable, sehingga biaya penghitungannya rendah.
Daur ulang GC?
Karena kumpulan konstanta string menyimpan referensi ke objek string bersama, apakah ini berarti objek tersebut tidak dapat didaur ulang?
Pertama-tama, objek yang dibagikan dalam pertanyaan umumnya relatif kecil. Sejauh yang saya tahu, masalah ini memang ada di versi sebelumnya, tetapi dengan diperkenalkannya referensi yang lemah, masalah ini seharusnya sudah hilang sekarang.
Mengenai masalah ini, Anda dapat mempelajari lebih lanjut tentang artikel ini yang diinternir Strings: Java Glosarium
penggunaan magang?
Prasyarat untuk menggunakan magang adalah Anda tahu bahwa Anda benar-benar perlu menggunakannya. Misalnya, kami memiliki jutaan catatan di sini, dan nilai tertentu dalam catatan adalah California, AS berkali-kali. Kami tidak ingin membuat jutaan objek string seperti itu. Kami dapat menggunakan intern untuk menyimpan hanya satu salinan di memori. Bisa. Untuk pemahaman yang lebih mendalam tentang magang, silakan lihat Analisis Mendalam tentang String#intern.
Apakah selalu ada pengecualian?
Tahukah Anda bahwa kode berikut akan membuat beberapa objek string dan menyimpan beberapa referensi di kumpulan konstanta string?
Copy kode kodenya sebagai berikut:
Tes string = "a" + "b" + "c";
Jawabannya adalah hanya satu objek yang dibuat dan hanya satu referensi yang disimpan di kumpulan konstan. Kita bisa mengetahuinya dengan menggunakan javap untuk mendekompilasi dan melihatnya.
Copy kode kodenya sebagai berikut:
17:02 $ javap -c TestInternedPoolGC
Dikompilasi dari "TestInternedPoolGC.java"
kelas publik TestInternedPoolGC memperluas java.lang.Object{
publik TestInternedPoolGC();
Kode:
0: memuat_0
1: invokespecial #1; //Metode java/lang/Object."<init>":()V
4: kembali
public static void main(java.lang.String[]) melempar java.lang.Exception;
Kode:
0: ldc #2; //String abc
2: toko_1
3: kembali
Apakah Anda melihat bahwa selama kompilasi, ketiga literal ini digabungkan menjadi satu? Ini sebenarnya merupakan optimasi yang menghindari pembuatan objek string yang berlebihan dan tidak menyebabkan masalah penyambungan string. Mengenai penyambungan string, Anda dapat melihat detail Java: Penyambungan string.