Javadoc tentang String.intern()
tidak memberikan banyak detail. (Singkatnya: Mengembalikan representasi kanonik string, memungkinkan string diinternalisasi untuk dibandingkan menggunakan ==
)
- Kapan saya akan menggunakan fungsi ini untuk mendukung
String.equals()
? - Apakah ada efek samping yang tidak disebutkan dalam Javadoc, yaitu lebih atau kurang optimalisasi oleh kompiler JIT?
- Apakah ada kegunaan lebih lanjut
String.intern()
?
Jawaban:
ketika Anda membutuhkan kecepatan karena Anda dapat membandingkan string dengan referensi (== lebih cepat dari sama dengan)
Kerugian utama adalah bahwa Anda harus ingat untuk memastikan bahwa Anda benar-benar melakukan magang () semua string yang akan Anda bandingkan. Sangat mudah untuk melupakan magang () semua string dan kemudian Anda bisa mendapatkan hasil yang salah membingungkan. Juga, demi semua orang, harap pastikan untuk mendokumentasikan dengan jelas bahwa Anda mengandalkan string yang diinternalisasi.
Kerugian kedua jika Anda memutuskan untuk menginternalisasi string adalah bahwa metode intern () relatif mahal. Itu harus mengelola kumpulan string unik sehingga melakukan sedikit pekerjaan yang adil (bahkan jika string sudah diinternalisasi). Jadi, berhati-hatilah dalam desain kode Anda sehingga Anda mis. Intern () semua string yang sesuai pada input sehingga Anda tidak perlu khawatir lagi.
(dari JGuru)
Kerugian ketiga (Java 7 atau kurang saja): Strings yang diinternir tinggal di ruang PermGen, yang biasanya cukup kecil; Anda dapat mengalami OutOfMemoryError dengan banyak ruang tumpukan gratis.
(dari Michael Borgwardt)
sumber
if (s1.equals(s2))
danif (i1 == i2)
minimal kecuali Anda memiliki banyak string panjang dengan karakter utama yang sama. Dalam sebagian besar penggunaan di dunia nyata (selain URL) string akan berbeda dalam beberapa karakter pertama. Lagi pula, rantai if-else adalah bau kode: gunakan enum dan peta functor.Ini hampir tidak ada hubungannya dengan perbandingan string. Interning string dimaksudkan untuk menghemat memori jika Anda memiliki banyak string dengan konten yang sama dalam aplikasi Anda. Dengan menggunakan
String.intern()
aplikasi ini hanya akan memiliki satu contoh dalam jangka panjang dan efek sampingnya adalah Anda dapat melakukan perbandingan kesetaraan referensi cepat alih-alih perbandingan string biasa (tetapi ini biasanya tidak disarankan karena benar-benar mudah dipecahkan dengan hanya lupa magang saja) satu contoh).sumber
str.intern()
saatstr
itu"Hello"
.String.intern()
jelas sampah yang dikumpulkan di JVM modern.Memori tidak pernah kehabisan berikut, karena aktivitas GC:
Lihat lebih banyak (dari saya) tentang mitos String.intern non GCed () .
sumber
OutOfMemoryException
- tidak, bukan kode di atas, di otak saya : tautan ke artikel javaturning, yang menunjuk ke artikel ini, yang menunjuk ke artikel javaturning, yang ... :-)Saya baru-baru ini menulis sebuah artikel tentang implementasi String.intern () di Java 6, 7 dan 8: String.intern di Java 6, 7 dan 8-pooling string .
Saya harap ini harus berisi informasi yang cukup tentang situasi saat ini dengan pengumpulan string di Jawa.
Pendeknya:
String.intern()
di Java 6, karena masuk ke PermGenString.intern()
di Java 7 & Java 8: ia menggunakan memori 4-5x lebih sedikit daripada menggulung kumpulan objek Anda sendiri-XX:StringTableSize
(standarnya mungkin terlalu kecil; tetapkan nomor Perdana)sumber
Membandingkan string dengan == jauh lebih cepat daripada dengan equals ()
5 Waktu lebih cepat, tetapi karena perbandingan String biasanya hanya mewakili sebagian kecil dari total waktu eksekusi suatu aplikasi, keuntungan keseluruhan jauh lebih kecil dari itu, dan keuntungan akhir akan terdilusi menjadi beberapa persen.
String.intern () tarik string dari Heap dan masukkan ke PermGen
String yang diinternalisasi diletakkan dalam area penyimpanan yang berbeda: Generasi Permanen yang merupakan area JVM yang dicadangkan untuk objek non-pengguna, seperti Kelas, Metode dan objek JVM internal lainnya. Ukuran area ini terbatas dan jauh lebih berharga daripada tumpukan. Karena area ini lebih kecil daripada Heap, ada lebih banyak kemungkinan untuk menggunakan semua ruang dan mendapatkan OutOfMemoryException.
String.intern () string adalah sampah yang dikumpulkan
Dalam versi baru JVM juga string internal adalah sampah yang dikumpulkan ketika tidak direferensikan oleh objek apa pun.
Mengingat 3 poin di atas, Anda dapat mengurangi bahwa String intern () dapat berguna hanya dalam beberapa situasi ketika Anda melakukan banyak perbandingan string, namun lebih baik tidak menggunakan string internal jika Anda tidak tahu persis apa yang Anda sedang melakukan ...
sumber
Mengingat mereka melakukan hal yang berbeda, mungkin tidak pernah.
String magang untuk alasan kinerja sehingga Anda dapat membandingkannya untuk kesetaraan referensi hanya akan bermanfaat jika Anda memegang referensi untuk string untuk sementara - string yang berasal dari input pengguna atau IO tidak akan diinternir.
Itu berarti dalam aplikasi Anda, Anda menerima input dari sumber eksternal dan memprosesnya menjadi objek yang memiliki nilai semantik - kata pengenal - tetapi objek itu memiliki tipe yang tidak dapat dibedakan dari data mentah, dan memiliki aturan yang berbeda tentang bagaimana programmer harus Gunakan.
Hampir selalu lebih baik untuk membuat
UserId
tipe yang diinternir (mudah untuk membuat mekanisme interning yang aman untuk thread) dan bertindak seperti enum terbuka, daripada membebanijava.lang.String
tipe tersebut dengan semantik referensi jika kebetulan itu adalah User ID.Dengan cara itu Anda tidak mendapatkan kebingungan antara apakah suatu String tertentu telah diinternir, dan Anda dapat merangkum setiap perilaku tambahan yang Anda butuhkan di enum terbuka.
sumber
Saya tidak mengetahui adanya keuntungan, dan jika ada dalam satu akan berpikir bahwa equals () itu sendiri akan menggunakan intern () internal (yang tidak).
Busting intern () mitos
sumber
intern
, dan alasan yang sangat bagus yangequals
tidak melakukannya secara default. Tautan yang Anda poskan adalah bollocks lengkap. Paragraf terakhir bahkan mengakui bahwaintern
memiliki skenario penggunaan yang valid: pemrosesan teks berat (misal pengurai). Menyimpulkan bahwa "[XYZ] berbahaya jika Anda tidak tahu apa yang Anda lakukan" begitu dangkal sehingga secara fisik sakit.Daniel Brückner benar sekali. String interning dimaksudkan untuk menghemat memori (heap). Sistem kami saat ini memiliki peta hash raksasa untuk menyimpan data tertentu. Sebagai skala sistem, hashmap akan cukup besar untuk membuat tumpukan kehabisan memori (seperti yang telah kami uji). Dengan menginternir semua string hasil duplikasi semua objek dalam hashmap, ini menghemat banyak ruang heap.
Juga di Java 7, string yang diinternir tidak lagi tinggal di PermGen tetapi malah menumpuk. Jadi Anda tidak perlu khawatir tentang ukurannya dan ya itu mengumpulkan sampah:
sumber
String
contoh. Ketika melihat konten mereka, saya melihat banyak duplikat dan memutuskan untuk beralihintern()
, yang menghemat ratusan MB.Saya tidak tahu tentang level JIT, tetapi ada dukungan bytecode langsung untuk kumpulan string , yang diimplementasikan secara ajaib dan efisien dengan
CONSTANT_String_info
struct khusus (tidak seperti kebanyakan objek lain yang memiliki representasi lebih umum).JVMS
JVMS 7 5.1 mengatakan :
Bytecode
Juga penting untuk melihat implementasi bytecode pada OpenJDK 7.
Jika kami mendekompilasi:
yang kita miliki di kolam konstan:
dan
main
:Perhatikan caranya:
0
dan3
:ldc #2
konstanta yang sama dimuat (literal)12
: instance string baru dibuat (dengan#2
sebagai argumen)35
:a
danc
dibandingkan sebagai objek biasa denganif_acmpne
Representasi string konstan cukup ajaib pada bytecode:
new String
)dan kutipan JVMS di atas tampaknya mengatakan bahwa setiap kali Utf8 menunjuk adalah sama, maka instance identik dimuat oleh
ldc
.Saya telah melakukan tes serupa untuk bidang, dan:
static final String s = "abc"
menunjuk ke tabel konstan melalui Atribut ConstantValueldc
Bonus : bandingkan dengan kelompok Integer , yang tidak memiliki dukungan bytecode langsung (yaitu tidak ada
CONSTANT_String_info
analog).sumber
Saya akan memeriksa intern dan == - perbandingan bukannya sama hanya dalam kasus sama-perbandingan menjadi hambatan dalam beberapa perbandingan string. Ini sangat tidak mungkin membantu dengan sejumlah kecil perbandingan, karena intern () tidak gratis. Setelah string interning secara agresif, Anda akan menemukan panggilan ke intern () semakin lambat.
sumber
Semacam kebocoran memori dapat berasal dari penggunaan
subString()
saat hasilnya kecil dibandingkan dengan string sumber dan objek memiliki umur panjang.Solusi normal adalah menggunakan
new String( s.subString(...))
tetapi ketika Anda memiliki kelas yang menyimpan hasil dari potensi / kemungkinansubString(...)
dan tidak memiliki kendali atas penelepon, Anda mungkin mempertimbangkan untuk menyimpanintern()
argumen String yang diteruskan ke konstruktor. Ini melepaskan potensi buffer besar.sumber
String magang berguna dalam kasus di mana
equals()
metode ini sering dipanggil karenaequals()
metode ini melakukan pemeriksaan cepat untuk melihat apakah objek sama pada awal metode.Ini biasanya terjadi pada saat mencari melalui
Collection
kode lain meskipun mungkin juga melakukan pemeriksaan kesetaraan string.Ada biaya yang terlibat untuk magang meskipun, saya melakukan microbenchmark dari beberapa kode dan menemukan bahwa proses magang meningkatkan runtime dengan faktor 10.
Tempat terbaik untuk melakukan magang biasanya ketika Anda membaca kunci yang disimpan di luar kode karena string dalam kode secara otomatis diinternir. Ini biasanya terjadi pada tahap inisialisasi aplikasi Anda untuk mencegah penalti pengguna pertama.
Tempat lain di mana hal itu dapat dilakukan adalah ketika memproses input pengguna yang dapat digunakan untuk melakukan pencarian kunci. Ini biasanya terjadi pada prosesor permintaan Anda, perhatikan bahwa string yang diinternir harus diturunkan.
Selain itu tidak ada gunanya melakukan magang di sisa kode karena umumnya tidak akan memberikan manfaat apa pun.
sumber
Saya akan memilih untuk tidak layak kerumitan pemeliharaan.
Sebagian besar waktu, tidak akan ada kebutuhan, dan tidak ada manfaat kinerja, kecuali jika kode Anda banyak bekerja dengan substring. Dalam hal ini kelas String akan menggunakan string asli plus offset untuk menghemat memori. Jika kode Anda banyak menggunakan substring, maka saya curiga itu hanya akan menyebabkan kebutuhan memori Anda meledak.
sumber
http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html
menegaskan yang
String.equals()
digunakan"=="
untuk membandingkanString
objek sebelumnya, menuruthttp://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html
itu membandingkan panjang Strings, dan kemudian isinya.
(Ngomong-ngomong, string kode produk dalam katalog penjualan memiliki panjang yang sama - BIC0417 adalah helm keselamatan pengendara sepeda, TIG0003 adalah harimau jantan dewasa yang hidup - Anda mungkin perlu segala macam lisensi untuk memesan salah satunya. Dan mungkin Anda lebih baik memesan helm pengaman secara bersamaan.)
Jadi sepertinya Anda mendapatkan manfaat dari mengganti Strings dengan
intern()
versi mereka , tetapi Anda mendapatkan keamanan - dan keterbacaan dan kepatuhan standar - -tanpa- menggunakan "==" untukequals()
pemrograman Anda. Dan sebagian besar dari apa yang akan saya katakan tergantung pada itu benar, jika itu benar.Tapi apakah
String.equals()
tes yang Anda berikan itu String dan bukan objek lain, sebelum digunakan"=="
? Saya tidak memenuhi syarat untuk mengatakan, tapi saya kira tidak, karena sebagian besarequals()
operasi seperti itu akan menjadi String to String, sehingga tes hampir selalu lulus. Memang, memprioritaskan "==" di dalamnyaString.equals()
menyiratkan keyakinan bahwa Anda sering membandingkan String dengan objek aktual yang sama.Saya harap tidak ada yang terkejut bahwa baris berikut menghasilkan hasil "salah":
Tetapi jika Anda beralih
i
kei.toString()
pada baris kedua, tentu saja itutrue
.Tempat-tempat di mana Anda mungkin berharap mendapat manfaat dari magang termasuk
Set
danMap
, tentu saja. Saya berharap bahwa string yang diinternir memiliki kode hash mereka di-cache ... Saya pikir itu akan menjadi persyaratan. Dan saya harap saya tidak hanya memberikan ide yang bisa menghasilkan jutaan dolar bagi saya. :-)Mengenai memori, jelas juga bahwa itu adalah batas penting jika volume String Anda besar, atau jika Anda ingin memori yang digunakan oleh kode program Anda menjadi sangat kecil. Jika volume Anda -distinct- Strings sangat besar, maka mungkin sudah saatnya untuk mempertimbangkan menggunakan kode program basis data khusus untuk mengelolanya, dan server basis data yang terpisah. Demikian juga, jika Anda dapat meningkatkan program kecil (yang perlu dijalankan dalam 10.000 instance secara bersamaan) dengan membuatnya tidak menyimpan Strings-nya sendiri sama sekali.
Rasanya boros membuat String baru dan kemudian langsung membuangnya untuk
intern()
penggantinya, tetapi tidak ada alternatif yang jelas, kecuali untuk menjaga String duplikat. Jadi sebenarnya biaya eksekusi adalah untuk mencari string Anda di kolam magang dan kemudian membiarkan pengumpul sampah untuk membuang yang asli. Dan jika itu adalah string literal maka itu sudah intern-ed pula.Saya bertanya-tanya apakah
intern()
dapat disalahgunakan oleh kode program jahat untuk mendeteksi apakah beberapa String dan referensi objek mereka sudah ada diintern()
kolam renang, dan karena itu ada di tempat lain di sesi Java, ketika itu seharusnya tidak diketahui. Tapi itu hanya mungkin ketika kode program sudah digunakan dengan cara yang dapat dipercaya, saya kira. Namun, ini adalah sesuatu yang perlu dipertimbangkan tentang perpustakaan pihak ketiga yang Anda sertakan dalam program Anda untuk menyimpan dan mengingat nomor PIN ATM Anda!sumber
Alasan sebenarnya untuk menggunakan intern bukan di atas. Anda bisa menggunakannya setelah kehabisan memori. Banyak string dalam program tipikal adalah String.substring () dari string besar lainnya [pikirkan untuk mengambil nama pengguna dari file 100K xml. Implementasi java adalah bahwa, substring memegang referensi ke string asli dan awal + akhir dalam string besar itu. (Pikiran di balik itu adalah penggunaan kembali dari string besar yang sama)
Setelah 1000 file besar, yang darinya Anda hanya menyimpan 1000 nama pendek, Anda akan menyimpan seluruh 1.000 file dalam memori! Solusi: dalam skenario ini cukup gunakan smallsubstring.intern ()
sumber
Saya menggunakan intern untuk menghemat memori, saya menyimpan sejumlah besar data String dalam memori dan pindah menggunakan intern () menghemat sejumlah besar memori. Sayangnya meskipun menggunakan banyak memori lebih sedikit, memori yang digunakan disimpan dalam memori PermGen bukan Heap dan sulit untuk menjelaskan kepada pelanggan bagaimana meningkatkan alokasi memori jenis ini.
Jadi apakah ada alternatif untuk magang () untuk mengurangi konsumsi memori, (manfaat == versus sama dengan kinerja bukanlah masalah bagi saya)
sumber
Mari kita hadapi itu: skenario kasus penggunaan utama adalah ketika Anda membaca aliran data (baik melalui aliran input, atau dari JDBC ResultSet) dan ada segudang Strings kecil yang diulangi di seluruh.
Berikut ini adalah sedikit trik yang memberi Anda kendali atas jenis mekanisme apa yang ingin Anda gunakan untuk menginternalisasi Strings dan kekekalan lainnya, dan contoh implementasi:
Saya sering menggunakannya ketika saya membaca bidang dari aliran atau dari ResultSets. Catatan:
LRUCache
berdasarkan cache sederhanaLinkedHashMap<K,V>
. Secara otomatis memanggilretrieve()
metode yang disediakan pengguna untuk semua kesalahan cache.Cara untuk menggunakannya adalah dengan membuatnya
LRUInternalizer
sebelum Anda membaca (atau membaca), menggunakannya untuk menginternalisasi Strings dan objek kecil yang tidak dapat diubah lainnya, kemudian membebaskannya. Sebagai contoh:sumber
Saya menggunakannya untuk men-cache konten sekitar 36000 kode yang menghubungkan ke nama-nama terkait. Saya menginternir string dalam cache karena banyak kode menunjuk ke string yang sama.
Dengan menginternir string dalam cache saya, saya memastikan bahwa kode yang menunjuk ke string yang sama sebenarnya menunjuk ke memori yang sama, sehingga menghemat ruang RAM saya.
Jika string yang diinternir benar-benar dikumpulkan, itu tidak akan bekerja untuk saya sama sekali. Ini pada dasarnya akan meniadakan tujuan magang. Milik saya tidak akan menjadi sampah yang dikumpulkan karena saya memegang referensi untuk masing-masing dan setiap string dalam cache.
sumber
Biaya magang string jauh lebih banyak daripada waktu yang dihemat dalam perbandingan stringA.equals (B) tunggal. Hanya gunakan itu (untuk alasan kinerja) ketika Anda berulang kali menggunakan variabel string yang tidak berubah yang sama. Sebagai contoh jika Anda secara teratur beralih pada daftar string yang stabil untuk memperbarui beberapa peta yang dikunci pada bidang string yang sama Anda bisa mendapatkan penghematan yang bagus.
Saya akan menyarankan menggunakan string magang untuk men-tweak kinerja ketika Anda mengoptimalkan bagian-bagian tertentu dari kode Anda.
Juga ingat bahwa String tidak dapat diubah dan jangan membuat kesalahan konyol
ingat untuk melakukannya
sumber
Jika Anda mencari pengganti String.intern yang tidak terbatas, juga sampah yang dikumpulkan, berikut ini berfungsi dengan baik untuk saya.
Tentu saja, jika Anda dapat memperkirakan kira-kira berapa banyak string yang berbeda, maka cukup gunakan String.intern () dengan -XX: StringTableSize = highEnoughValue .
sumber