Apakah praktik yang baik untuk menggunakan java.lang.String.intern ()?

194

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()?
Daniel Rikowski
sumber
14
Calling intern () memiliki dampak kinerja sendiri, menggunakan intern () untuk meningkatkan kinerja perlu diuji untuk memastikan itu benar-benar mempercepat program Anda secara signifikan sebanding dengan kompleksitas tambahan. Anda juga dapat menggunakan ini untuk mengurangi konsumsi memori untuk tabel besar dengan nilai repedatif yang relatif. Namun, dalam kedua kasus ada opsi lain yang mungkin lebih baik.
Peter Lawrey
Ya, intern () memiliki dampak kinerja sendiri. Terutama karena biaya magang () meningkat secara linier saat Anda magang string dan menyimpan referensi kepada mereka. Setidaknya pada matahari / oracle 1.6.0_30 vm.
lacroix1547

Jawaban:

125

Kapan saya akan menggunakan fungsi ini untuk mendukung String.equals ()

ketika Anda membutuhkan kecepatan karena Anda dapat membandingkan string dengan referensi (== lebih cepat dari sama dengan)

Apakah ada efek samping yang tidak disebutkan dalam Javadoc?

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)

dfa
sumber
64
Kerugian ketiga: Strings yang diinternir tinggal di ruang PermGen, yang biasanya cukup kecil; Anda dapat mengalami OutOfMemoryError dengan banyak ruang tumpukan gratis.
Michael Borgwardt
15
VM yang lebih baru AFAIK juga mengumpulkan ruang PermGen.
Daniel Rikowski
31
Intern adalah tentang manajemen memori, bukan kecepatan perbandingan. Perbedaan antara if (s1.equals(s2))dan if (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.
kdgregory
25
Anda masih dapat menggunakan sintaks s1.equals di seluruh program Anda, JANGAN gunakan ==, .equals gunakan == secara internal untuk evaluasi hubung singkat
gtrak
15
Michael Borgwardt TIDAK mengatakan bahwa string yang diinternir tidak dapat dikumpulkan dari sampah. Dan itu adalah pernyataan SALAH. Apa yang dikatakan komentar Michael (dengan benar) lebih halus dari itu.
Stephen C
193

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).

Daniel Brückner
sumber
4
Itu tidak benar. Interning of Strings selalu terjadi, secara otomatis, ketika setiap ekspresi string dievaluasi. Selalu ada satu salinan untuk setiap rangkaian karakter unik yang digunakan & "dibagikan secara internal" jika terjadi beberapa penggunaan. Memanggil String.intern () tidak membuat ini semua terjadi - itu hanya mengembalikan representasi kanonik internal. Lihat javadoc.
Glen Best
16
Perlu diklarifikasi - magang selalu terjadi secara otomatis untuk string konstan waktu kompilasi (literal & ekspresi tetap). Selain itu terjadi ketika String.intern () dipanggil pada runtime yang secara dinamis dievaluasi Strings.
Glen Best
Jadi maksud Anda, jika ada 1000 objek "Hello" di Heap dan saya melakukan intern () pada salah satunya, maka sisanya 999 objek akan dihancurkan secara otomatis?
Arun Raaj
@ArunRaaj tidak, Anda akan memiliki 1000 masih di tumpukan, dan satu lagi di kolam magang, yang dapat siap untuk digunakan kembali nanti str.intern()saat stritu "Hello".
Matthieu
37

String.intern()jelas sampah yang dikumpulkan di JVM modern.
Memori tidak pernah kehabisan berikut, karena aktivitas GC:

// java -cp . -Xmx128m UserOfIntern

public class UserOfIntern {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextLong());
        while (true) {
            String s = String.valueOf(random.nextLong());
            s = s.intern();
        }
    }
}

Lihat lebih banyak (dari saya) tentang mitos String.intern non GCed () .

Gili Nachum
sumber
26
OutOfMemoryException- tidak, bukan kode di atas, di otak saya : tautan ke artikel javaturning, yang menunjuk ke artikel ini, yang menunjuk ke artikel javaturning, yang ... :-)
user85421
Meskipun Anda dapat melihat bahwa pos telah diedit untuk menambahkan tautan itu;)
Riking
3
Anda mungkin ingin menyebutkan bahwa Anda juga penulis referensi eksternal yang Anda tautkan.
Thorbjørn Ravn Andersen
11
@Carlos menautkan referensi eksternal yang menautkan kembali ke stackoverflow harus menyebabkan .. Stackoverflow :)
Seiti
2
Referensi Circular @Seiti mudah dideteksi hari ini: p
Ajay
16

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:

  • Hindari String.intern()di Java 6, karena masuk ke PermGen
  • Lebih suka String.intern()di Java 7 & Java 8: ia menggunakan memori 4-5x lebih sedikit daripada menggulung kumpulan objek Anda sendiri
  • Pastikan untuk menyetel -XX:StringTableSize(standarnya mungkin terlalu kecil; tetapkan nomor Perdana)
mik1
sumber
3
Tolong jangan hanya mengirim tautan ke blog Anda, ini dianggap oleh beberapa orang sebagai SPAM. Link blog Plus memiliki kecenderungan untuk mati 404 kematian. Silakan meringkas artikel Anda sebaris di sini, atau tinggalkan tautan itu dalam komentar untuk pertanyaan.
Mat
3
Terima kasih telah menulisnya @ mik1! Artikel yang sangat informatif, jelas, dan terkini. (Saya kembali ke sini bermaksud untuk memposting tautannya sendiri.)
Luke Usherwood
1
Terima kasih telah menyebutkan -XX arg. Anda juga dapat menggunakan ini untuk melihat statistik tabel: -XX: + PrintStringTableStatistics
csadler
13

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 ...

aleroot
sumber
1
Sebagai tambahan, Heap memory exception terkadang dapat dipulihkan, terutama dalam model berulir seperti aplikasi web. Ketika permgen habis, suatu aplikasi biasanya akan secara permanen tidak berfungsi dan seringkali sumber daya akan hancur sampai terbunuh.
Taylor
7

Kapan saya akan menggunakan fungsi ini untuk mendukung String.equals ()

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 UserIdtipe yang diinternir (mudah untuk membuat mekanisme interning yang aman untuk thread) dan bertindak seperti enum terbuka, daripada membebani java.lang.Stringtipe 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.

Pete Kirkham
sumber
6

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

benda
sumber
7
Meskipun Anda mengatakan bahwa Anda tidak mengetahui adanya keuntungan, tautan Anda yang diposting mengidentifikasi perbandingan melalui == sebagai 5x lebih cepat dan dengan demikian penting untuk kode
pemain yang
3
Ketika Anda memiliki banyak perbandingan teks untuk dilakukan, Anda pada akhirnya akan kehabisan ruang PermGen. Ketika tidak ada begitu banyak membandingkan teks untuk melakukan perbedaan kecepatan tidak masalah. Either way, hanya saja jangan magang () string Anda. Itu tidak layak.
Bombe
Lebih lanjut dikatakan bahwa keuntungan relatif keseluruhan biasanya akan kecil.
objek
Saya tidak berpikir logika semacam itu valid. Tautan yang bagus!
Daniel Rikowski
1
@DR: logika apa? Itu salah besar. @objects: maaf tapi argumen Anda tidak punya alasan. Ada alasan yang sangat bagus untuk digunakan intern, dan alasan yang sangat bagus yang equalstidak melakukannya secara default. Tautan yang Anda poskan adalah bollocks lengkap. Paragraf terakhir bahkan mengakui bahwa internmemiliki 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.
Konrad Rudolph
4

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:

Dalam JDK 7, string yang diinternir tidak lagi dialokasikan dalam generasi permanen heap Jawa, tetapi sebaliknya dialokasikan di bagian utama heap Jawa (dikenal sebagai generasi muda dan tua), bersama dengan objek lain yang dibuat oleh aplikasi . Perubahan ini akan menghasilkan lebih banyak data yang berada di heap utama Java, dan lebih sedikit data dalam generasi permanen, dan dengan demikian mungkin memerlukan ukuran tumpukan yang harus disesuaikan. Sebagian besar aplikasi hanya akan melihat perbedaan yang relatif kecil dalam penggunaan tumpukan karena perubahan ini, tetapi aplikasi yang lebih besar yang memuat banyak kelas atau menggunakan metode String.intern () akan melihat perbedaan yang lebih signifikan.

xli
sumber
Saya harus kedua bahwa: pada perangkat lunak saya, tumpukan tumpukan menunjukkan bahwa sebagian besar ruang tumpukan digunakan oleh Stringcontoh. Ketika melihat konten mereka, saya melihat banyak duplikat dan memutuskan untuk beralih intern(), yang menghemat ratusan MB.
Matthieu
4

Apakah ada efek samping yang tidak disebutkan dalam Javadoc, yaitu lebih atau kurang optimalisasi oleh kompiler JIT?

Saya tidak tahu tentang level JIT, tetapi ada dukungan bytecode langsung untuk kumpulan string , yang diimplementasikan secara ajaib dan efisien dengan CONSTANT_String_infostruct khusus (tidak seperti kebanyakan objek lain yang memiliki representasi lebih umum).

JVMS

JVMS 7 5.1 mengatakan :

Literal string adalah referensi ke instance dari String kelas, dan diturunkan dari struktur CONSTANT_String_info (§4.4.3) dalam representasi biner dari kelas atau antarmuka. Struktur CONSTANT_String_info memberikan urutan titik kode Unicode yang membentuk string literal.

Bahasa pemrograman Java mensyaratkan bahwa literal string identik (yaitu, literal yang berisi urutan titik kode yang sama) harus merujuk ke instance String kelas yang sama (JLS §3.10.5). Selain itu, jika metode String.intern dipanggil pada sembarang string, hasilnya adalah referensi ke instance kelas yang sama yang akan dikembalikan jika string itu muncul sebagai literal. Dengan demikian, ekspresi berikut harus memiliki nilai true:

("a" + "b" + "c").intern() == "abc"

Untuk mendapatkan string literal, Java Virtual Machine memeriksa urutan poin kode yang diberikan oleh struktur CONSTANT_String_info.

  • Jika metode String.intern sebelumnya telah dipanggil pada instance String kelas yang berisi urutan titik kode Unicode yang identik dengan yang diberikan oleh struktur CONSTANT_String_info, maka hasil derivasi string literal adalah referensi ke instance String String kelas yang sama.

  • Jika tidak, instance baru String kelas dibuat yang berisi urutan titik kode Unicode yang diberikan oleh struktur CONSTANT_String_info; referensi ke instance kelas adalah hasil derivasi string literal. Akhirnya, metode intern instance String baru dipanggil.

Bytecode

Juga penting untuk melihat implementasi bytecode pada OpenJDK 7.

Jika kami mendekompilasi:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

yang kita miliki di kolam konstan:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

dan main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Perhatikan caranya:

  • 0dan 3: ldc #2konstanta yang sama dimuat (literal)
  • 12: instance string baru dibuat (dengan #2sebagai argumen)
  • 35: adan cdibandingkan sebagai objek biasa denganif_acmpne

Representasi string konstan cukup ajaib pada bytecode:

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 ConstantValue
  • bidang non-final tidak memiliki atribut itu, tetapi masih dapat diinisialisasi dengan ldc

Bonus : bandingkan dengan kelompok Integer , yang tidak memiliki dukungan bytecode langsung (yaitu tidak ada CONSTANT_String_infoanalog).

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
2

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.

Mikko Maunu
sumber
2

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 / kemungkinan subString(...)dan tidak memiliki kendali atas penelepon, Anda mungkin mempertimbangkan untuk menyimpan intern()argumen String yang diteruskan ke konstruktor. Ini melepaskan potensi buffer besar.

eremmel
sumber
Menarik, tapi mungkin ini tergantung implementasi.
akostadinov
1
Potensi kebocoran memori yang disebutkan di atas tidak terjadi di java 1.8 dan 1.7.06 (dan yang lebih baru) lihat Perubahan ke representasi internal String yang dibuat di Java 1.7.0_06 .
eremmel
yang mengonfirmasi optimasi mikro hanya diterapkan bila perlu setelah kinerja dan / atau profil memori. Terima kasih.
akostadinov
2

String magang berguna dalam kasus di mana equals()metode ini sering dipanggil karena equals()metode ini melakukan pemeriksaan cepat untuk melihat apakah objek sama pada awal metode.

if (this == anObject) {
    return true;
}

Ini biasanya terjadi pada saat mencari melalui Collectionkode 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.

Archimedes Trajano
sumber
1

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.

wm_eddie
sumber
1

http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html

menegaskan yang String.equals()digunakan "=="untuk membandingkan Stringobjek sebelumnya, menurut

http://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 "==" untuk equals()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 besar equals()operasi seperti itu akan menjadi String to String, sehingga tes hampir selalu lulus. Memang, memprioritaskan "==" di dalamnya String.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":

    Integer i = 1;
    System.out.println("1".equals(i));

Tetapi jika Anda beralih ike i.toString()pada baris kedua, tentu saja itu true.

Tempat-tempat di mana Anda mungkin berharap mendapat manfaat dari magang termasuk Setdan Map, 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 di intern()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!

Robert Carnegie
sumber
0

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 ()

asaf
sumber
Mengapa tidak membuat string baru dari substring jika Anda memerlukannya?
Thorbjørn Ravn Andersen
0

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)

Paul Taylor
sumber
0

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:

/**
 * Extends the notion of String.intern() to different mechanisms and
 * different types. For example, an implementation can use an
 * LRUCache<T,?>, or a WeakHashMap.
 */
public interface Internalizer<T> {
    public T get(T obj);
}
public static class LRUInternalizer<T> implements Internalizer<T> {
    private final LRUCache<T, T> cache;
    public LRUInternalizer(int size) {
        cache = new LRUCache<T, T>(size) {
            private static final long serialVersionUID = 1L;
            @Override
            protected T retrieve(T key) {
                return key;
            }
        };
    }
    @Override
    public T get(T obj) {
        return cache.get(obj);
    }
}
public class PermGenInternalizer implements Internalizer<String> {
    @Override
    public String get(String obj) {
        return obj.intern();
    }
}

Saya sering menggunakannya ketika saya membaca bidang dari aliran atau dari ResultSets. Catatan: LRUCacheberdasarkan cache sederhana LinkedHashMap<K,V>. Secara otomatis memanggil retrieve()metode yang disediakan pengguna untuk semua kesalahan cache.

Cara untuk menggunakannya adalah dengan membuatnya LRUInternalizersebelum Anda membaca (atau membaca), menggunakannya untuk menginternalisasi Strings dan objek kecil yang tidak dapat diubah lainnya, kemudian membebaskannya. Sebagai contoh:

Internalizer<String> internalizer = new LRUInternalizer(2048);
// ... get some object "input" that stream fields
for (String s : input.nextField()) {
    s = internalizer.get(s);
    // store s...
}
Pierre D
sumber
0

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.

Rodney P. Barbati
sumber
Tidak, semua string yang sama diinternir yang ada dalam memori pada waktu tertentu, masih akan menjadi objek yang sama. Ini akan menjadi objek yang berbeda dari string yang sama yang ada di memori sebelum dikumpulkan. Tapi ini tidak masalah karena string lama sudah tidak ada lagi.
bdruemen
0

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

String a = SOME_RANDOM_VALUE
a.intern()

ingat untuk melakukannya

String a = SOME_RANDOM_VALUE.intern()
penggerutu
sumber
0

Jika Anda mencari pengganti String.intern yang tidak terbatas, juga sampah yang dikumpulkan, berikut ini berfungsi dengan baik untuk saya.

private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}

Tentu saja, jika Anda dapat memperkirakan kira-kira berapa banyak string yang berbeda, maka cukup gunakan String.intern () dengan -XX: StringTableSize = highEnoughValue .

bdruemen
sumber
SoftRef akan lebih masuk akal.
vach
@vach Dengan menggunakan WeakReference (bukan SoftReference), memori dibebaskan lebih awal sehingga alokasi lain mungkin berjalan lebih cepat. Tergantung pada apa lagi aplikasi tersebut lakukan, salah satu bisa masuk akal.
bdruemen