Java.lang.String.intern () kullanmak iyi bir uygulama mı?


194

Hakkında Javadoc String.intern()fazla ayrıntı vermez. (Özetle: Dizenin kanonik bir temsilini döndürerek, iç içe dizelerin kullanılarak karşılaştırılmasını sağlar ==)

  • Bu işlevi ne zaman lehine kullanırım String.equals()?
  • Javadoc'da belirtilmeyen yan etkiler var mı, yani JIT derleyicisi tarafından az çok optimizasyon mu var?
  • Bunun başka kullanımları var String.intern()mı?

14
Intern () öğesini çağırmanın kendi etkisi vardır, intern () kullanarak performansı artırmak için programın gerçekten ekstra karmaşıklığa değecek şekilde önemli ölçüde hızlandırıldığından emin olmak için test edilmesi gerekir. Ayrıca relabdatif repedatif değerleri olan büyük tablolarda bellek tüketimini azaltmak için de kullanabilirsiniz. Ancak, her iki durumda da daha iyi olabilecek başka seçenekler de vardır.
Peter Lawrey

Evet, intern () 'in kendi performans etkisi vardır. Özellikle, stajyer dizeleri ve onlara bir referans tutmak gibi intern () maliyeti lineer olarak artar çünkü. En azından bir güneş / kehanette 1.6.0_30 vm.
lacroix1547

Yanıtlar:


125

Ne zaman String.equals () lehine bu işlevi kullanmak istiyorsunuz

Dizeleri referans ile karşılaştırabildiğiniz için hıza ihtiyacınız olduğunda (== eşittir daha hızlıdır)

Javadoc'ta belirtilmeyen yan etkiler var mı?

Birincil dezavantaj, karşılaştıracağınız tüm dizeleri stajyer () yaptığınızdan emin olmanız gerektiğidir. Tüm dizeleri stajyer () unutmak kolaydır ve daha sonra kafa karıştırıcı yanlış sonuçlar alabilirsiniz. Ayrıca, herkesin iyiliği için, lütfen içselleştirilen dizelere güvendiğinizi çok açık bir şekilde belgelediğinizden emin olun.

Dizeleri içselleştirmeye karar verirseniz ikinci dezavantaj, intern () yönteminin nispeten pahalı olmasıdır. Eşsiz dizeler havuzunu yönetmelidir, böylece oldukça fazla iş yapar (dize zaten içselleştirilmiş olsa bile). Bu nedenle, kod tasarımınızda dikkatli olun, böylece girişte tüm uygun dizeleri stajyer () edin, böylece artık endişelenmenize gerek kalmaz.

(JGuru'dan)

Üçüncü dezavantaj (yalnızca Java 7 veya daha az): stajyer Dizeler genellikle oldukça küçük olan PermGen alanında yaşar; bol miktarda boş yığın alanı olan bir OutOfMemoryError ile karşılaşabilirsiniz.

(Michael Borgwardt'tan)


64
Üçüncü bir dezavantaj: stajyer Dizeler genellikle oldukça küçük olan PermGen uzayında yaşar; bol miktarda boş yığın alanı olan bir OutOfMemoryError ile karşılaşabilirsiniz.
Michael Borgwardt

15
AFAIK'ın yeni VM'leri de PermGen alanını toplamaktadır.
Daniel Rikowski

31
Stajyer, karşılaştırma hızı ile değil hafıza yönetimi ile ilgilidir. Aynı önde gelen karakterlere sahip uzun dizeleriniz olmadığı sürece if (s1.equals(s2))ve arasındaki fark if (i1 == i2)minimumdur. Çoğu gerçek dünya kullanımında (URL'ler dışında) dizeler ilk birkaç karakter arasında farklılık gösterir. Ve eğer uzun zincirler zaten bir kod kokusudur: numaralandırmalar ve işlev haritaları kullanın.
kdgregory

25
yine de programınız boyunca s1.equals sözdizimini kullanabilirsiniz, DONT ==, .equals kullanın == dahili olarak kısa devre değerlendirmesi için
gtrak

15
Michael Borgwardt stajyer dizelerin çöp toplanamayacağını söylemedi. Ve bu YANLIŞ bir iddia. Michael'ın (doğru) yorumlarının söylediği bundan daha incedir.
Stephen C

193

Bunun dize karşılaştırmasıyla (neredeyse) hiçbir ilgisi yoktur. String interning , uygulamanızda aynı içeriğe sahip birçok dizeniz varsa bellek tasarrufu yapmak için tasarlanmıştır. String.intern()Uygulamayı kullanarak uzun vadede sadece bir örneği olacaktır ve bir yan etkisi, sıradan dize karşılaştırması yerine hızlı referans eşitliği karşılaştırması yapabilmenizdir (ancak bu genellikle tavsiye edilmez, çünkü sadece stajyeri unutarak kırılması gerçekten kolaydır tek bir örnek).


4
Bu doğru değil. Dizelerin staj edilmesi, her dize ifadesi değerlendirildiğinde daima otomatik olarak gerçekleşir. Kullanılan her benzersiz karakter dizisi için her zaman bir kopya vardır ve birden fazla kullanım gerçekleşirse "dahili olarak paylaşılır". String.intern () öğesini çağırmak bunların tümünü gerçekleştirmez - sadece iç standart temsili döndürür. Bakınız javadoc.
Glen Best

16
Açıklığa kavuşturulması gerekiyor - derleme zamanı sabit Dizeleri (değişmez değerler ve sabit ifadeler) için stajyerlik her zaman otomatik olarak gerçekleşir. Ayrıca, çalışma zamanı dinamik olarak değerlendirilen Dizelerde String.intern () çağrıldığında oluşur.
Glen Best

Yani, Öbek içinde "Merhaba" 1000 nesneler varsa ve ben bunlardan birinde stajyer () yapmak, o zaman geri kalanı 999 nesneleri otomatik olarak yok edilecek?
Arun Raaj

Hayır @ArunRaaj, yine yığın, üzerinde 1000 olacak ve ekstra bir tek sonradan tarafından yeniden kullanıma hazır olabilir stajyer havuzunda, str.intern()zaman strolduğunu "Hello".
Matthieu

37

String.intern()modern JVM'lerde kesinlikle toplanan çöptür.
GC etkinliği nedeniyle aşağıdaki ASLA bellek yetersiz kalmaz:

// 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();
        }
    }
}

GCed olmayan String.intern () efsanesi hakkında daha fazla bilgi (benden ) .


26
OutOfMemoryException- hayır, yukarıdaki kod değil, beynimde : Bu makaleye işaret eden javaturning makalesine bağlantı, javaturning makalesine işaret eden, ... :-)
user85421


3
Bağlandığınız harici referansın da yazarı olduğunuzu belirtmek isteyebilirsiniz.
Thorbjørn Ravn Andersen

11
@ Stackoverflow geri bağlantı harici bir referans bağlayan Carlos bir neden olabilir .. Stackoverflow :)
Seiti

2
@Seiti Dairesel referanslar bu günlerde kolayca tespit edilebilir: p
Ajay

16

Son zamanlarda Java 6, 7 ve 8 String.intern () uygulaması hakkında bir makale yazdım: Java 6, 7 ve 8 - string havuzunda String.intern .

Umarım Java dize havuzu ile mevcut durum hakkında yeterli bilgi içermelidir.

Kısaca:

  • String.intern()Java 6'da kaçının , çünkü PermGen'e gider
  • String.intern()Java 7 ve Java 8'de tercih edin: kendi nesne havuzunuzu döndürmekten 4-5 kat daha az bellek kullanır
  • -XX:StringTableSizeAyarladığınızdan emin olun (varsayılan değer muhtemelen çok küçüktür; bir Prime numarası ayarlayın)

3
Lütfen blogunuza bağlantı göndermeyin, bazıları SPAM olarak kabul edilir. Ayrıca blog bağlantıları 404 ölümle ölme eğiliminde. Lütfen makalenizi burada satır içinde özetleyin veya bu bağlantıyı soruya yorum olarak bırakın.
Mat

3
@ Mik1 yazdığınız için teşekkürler! Çok bilgilendirici, açık ve güncel makale. (Buraya kendime bir bağlantı göndermek niyetiyle geldim.)
Luke Usherwood

1
-XX değişkeninden bahsettiğiniz için teşekkür ederiz. Bunu tablo istatistiklerini görmek için de kullanabilirsiniz: -XX: + PrintStringTableStatistics
csadler

13

== ile dizeleri karşılaştırmak eşittir () ile karşılaştırıldığında çok daha hızlıdır

5 Zaman daha hızlıdır, ancak Dize karşılaştırması genellikle bir uygulamanın toplam yürütme süresinin sadece küçük bir yüzdesini temsil ettiğinden, toplam kazanç bundan çok daha küçüktür ve son kazanç yüzde birkaçına seyreltilecektir.

String.intern (), dizeyi Heap'ten çekin ve PermGen'e koyun

Dahili dize farklı bir depolama alanına yerleştirilir: JVM'nin Sınıflar, Yöntemler ve diğer dahili JVM nesneleri gibi kullanıcı olmayan nesneler için ayrılmış bir alanı olan Kalıcı Nesil . Bu alanın boyutu sınırlıdır ve yığından çok değerlidir. Bu alan Heap'tan daha küçük olduğundan, tüm alanı kullanma ve OutOfMemoryException alma olasılığı daha yüksektir.

String.intern () dize çöp toplanır

JVM'nin yeni versiyonlarında ayrıca herhangi bir nesne tarafından referans alınmadığında içsel dize çöp toplanır.

Yukarıdaki 3 noktayı aklınızda tutarak String intern () işlevinin çok sayıda dize karşılaştırması yaptığınızda yalnızca birkaç durumda yararlı olabileceğini düşürebilirsiniz, ancak tam olarak ne yaptığınızı bilmiyorsanız dahili dize kullanmamanız daha iyi yapıyorlar ...



1
Sadece eklemek için, Yığın bellek istisnaları bazen, özellikle web uygulamaları gibi dişli modellerde kurtarılabilir. Permen tükendiğinde, bir uygulama tipik olarak kalıcı olarak işlevsel olmayacak ve genellikle öldürülünceye kadar thrash kaynağı olacaktır.
Taylor

7

Ne zaman String.equals () lehine bu işlevi kullanmak istiyorsunuz

Farklı şeyler yaptıkları göz önüne alındığında, muhtemelen asla.

Performans nedenlerinden dolayı staj dizileri, böylece referans eşitliği ile karşılaştırabilmeniz için sadece bir süre dizelere referanslar tutuyorsanız yararlı olacaktır - kullanıcı girişinden veya IO'dan gelen dizeler stajyer olmayacaktır.

Bu, uygulamanızda harici bir kaynaktan girdi aldığınız ve bunu semantik değere sahip bir nesneye işlediğiniz anlamına gelir - bir tanımlayıcı demek - ancak bu nesnenin ham verilerden ayırt edilemeyen bir türü vardır ve programcının nasıl olması gerektiğine ilişkin farklı kuralları vardır kullanın.

UserIdİnterned (iş parçacığı için güvenli bir genel stajyer mekanizması oluşturmak kolaydır) ve açık bir enum gibi davranan bir tür oluşturmak neredeyse her zaman daha iyidir java.lang.String.

Bu şekilde, belirli bir String'in stajyere dahil edilip edilmediği arasında karışıklık olmaz ve açık numarada istediğiniz ek davranışları kapsülleyebilirsiniz.


6

Herhangi bir avantajın farkında değilim ve eğer bir tane varsa, equals () 'in kendi içinde intern () kullanacağını düşünürdü (ki bunu yapmaz).

Stajyerlik () mitleri


7
Herhangi bir avantajın farkında olmadığınızı söylemenize rağmen, yayınlanan bağlantınız == yoluyla karşılaştırmayı 5 kat daha hızlı ve böylece metin merkezli performans kodu için önemli olarak tanımlar
Brian Agnew

3
Yapacak çok fazla metin karşılaştırmanız olduğunda, sonunda PermGen alanınız bitecek. Çok fazla metin karşılaştırması olmadığında hız farkı önemli değil. Her iki durumda da, sadece dizelerinizi stajyerleştirmeyin. Buna değmez.
Bombe

Ayrıca genel göreceli kazancın tipik olarak küçük olacağını söylemeye devam eder.
nesneler

Bu tür bir mantığın geçerli olduğunu düşünmüyorum. Yine de iyi bir bağlantı!
Daniel Rikowski

1
@DR: hangi mantık? Bu büyük bir yanılgı. @objects: üzgünüm ama argümanlarınız sebeplerden yoksun. Kullanmak için çok iyi nedenler internve equalsvarsayılan olarak bunu yapmayan çok iyi nedenler var . Gönderdiğiniz bağlantı tam bir kilitlemedir. Son paragraf intern, geçerli bir kullanım senaryosuna sahip olduğunu da kabul eder : ağır metin işleme (örn. Ayrıştırıcı). “[XYZ] 'in ne yaptığınızı bilmiyorsanız tehlikeli olduğu sonucuna varmak o kadar banal ki fiziksel olarak acıtıyor.
Konrad Rudolph

4

Daniel Brückner kesinlikle haklı. String interning, bellek tasarrufu (yığın) içindir. Sistemimiz şu anda belirli verileri tutmak için dev bir hashiye sahiptir. Sistem ölçeklendikçe, hashmap yığınının bellekte kalmasını sağlayacak kadar büyük olacaktır (test ettiğimiz gibi). Çoğaltılan tüm dizeleri hashmap içindeki tüm nesneleri yerleştirerek, bize önemli miktarda yığın alanı kazandırır.

Ayrıca Java 7'de, stajlı dizeler artık PermGen'de yaşamıyor, bunun yerine yığın yapıyor. Yani boyutu hakkında endişelenmenize gerek yok ve evet çöp toplanıyor:

JDK 7'de, sabit dizeler artık Java yığınının kalıcı neslinde değil, uygulama tarafından oluşturulan diğer nesnelerle birlikte Java yığınının ana bölümünde (genç ve eski kuşaklar olarak bilinir) ayrılır. . Bu değişiklik, ana Java yığınında daha fazla veriye ve kalıcı oluşturmada daha az veriye neden olur ve bu nedenle yığın boyutlarının ayarlanmasını gerektirebilir. Çoğu uygulama, bu değişiklik nedeniyle yığın kullanımında yalnızca nispeten küçük farklılıklar görür, ancak birçok sınıf yükleyen veya String.intern () yöntemini yoğun kullanan daha büyük uygulamalar daha önemli farklılıklar görür.


İkincisi olmalıyım: yazılımımda, bir yığın dökümü çoğu yığın alanının Stringörnekler tarafından kullanıldığını gösterdi . İçeriğine bakarken, birçok kopya gördüm ve intern()yüzlerce MB tasarruf sağlayan geçiş yapmaya karar verdim .
Matthieu

4

Javadoc'da belirtilmeyen yan etkiler var mı, yani JIT derleyicisi tarafından az çok optimizasyon mu var?

JIT seviyesi hakkında bilmiyorum, ama sihirli bir ve özel bir yapı (daha genel gösterimleri olan diğer nesnelerin aksine) ile verimli bir şekilde uygulanan dize havuzu için doğrudan bayt kodu desteği vardırCONSTANT_String_info .

JVM'lerle

JVMS 7 5.1 diyor ki :

Dize değişmezi, String sınıfının bir örneğine başvurudır ve bir sınıfın veya arabirimin ikili gösterimindeki CONSTANT_String_info yapısından (§4.4.3) türetilir. CONSTANT_String_info yapısı, dizgi değişmezini oluşturan Unicode kod noktalarının sırasını verir.

Java programlama dili, aynı dize değişmezlerinin (yani, aynı kod noktaları sırasını içeren değişmez değerlerin) aynı Dize sınıfı örneğine (JLS §3.10.5) başvurmasını gerektirir. Ayrıca, String.intern yöntemi herhangi bir dizede çağrılırsa, sonuç, bu dize değişmez olarak görünürse döndürülecek aynı sınıf örneğine başvurudur. Bu nedenle, aşağıdaki ifade true değerine sahip olmalıdır:

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

Bir dizgi değişmezi türetmek için Java Sanal Makinesi, CONSTANT_String_info yapısı tarafından verilen kod noktalarının sırasını inceler.

  • String.intern yöntemi daha önce, CONSTANT_String_info yapısı tarafından verilenle aynı Unicode kod noktaları dizisini içeren bir sınıf String örneğinde çağrılmışsa, dize değişmez türetmesinin sonucu, aynı sınıf String örneğine başvurudur.

  • Aksi takdirde, CONSTANT_String_info yapısı tarafından verilen Unicode kod noktalarının sırasını içeren yeni bir String sınıfı örneği oluşturulur; bu sınıf örneğine başvuru, dizgi değişmezinin türetilmesinin sonucudur. Son olarak, yeni String örneğinin stajyer yöntemi çağrılır.

Bytecode

OpenJDK 7'deki bayt kodu uygulamasına bakmak da öğreticidir.

Biz kodalar:

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);
    }
}

sürekli havuzumuz var:

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

ve 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

Nasıl yapıldığına dikkat edin:

  • 0ve 3: aynı ldc #2sabit yüklenir (değişmez değerler)
  • 12: yeni bir dize örneği oluşturulur ( #2argüman olarak)
  • 35: ave cnormal nesnelerle karşılaştırılır.if_acmpne

Sabit dizelerin temsili bayt kodunda oldukça büyülüdür:

ve yukarıdaki JVMS alıntısı, Utf8'in işaret ettiği her zaman aynı olduğunda, aynı örneklerin yüklendiğini söylüyor ldc.

Alanlar için benzer testler yaptım ve:

  • static final String s = "abc"ConstantValue Özelliği aracılığıyla sabit tabloyu gösterir
  • son olmayan alanlar bu özelliğe sahip değildir, ancak yine de ile başlatılabilir ldc

Bonus : bunu doğrudan bayt kodu desteği olmayan (yani analog olmayan ) Tamsayı havuzuyla karşılaştırın CONSTANT_String_info.


2

Stajyerin ve == - karşılaştırma yerine eşittir-karşılaştırma dize birden çok karşılaştırmada darboğaz olması durumunda eşittir incelemek istiyorum. Bu, az sayıda karşılaştırmaya yardımcı olma olasılığı düşüktür, çünkü intern () ücretsiz değildir. Agresif bir şekilde dizilmiş dizelerden sonra intern () çağrılarının yavaşladığını göreceksiniz.


2

subString()Sonuç, kaynak dizgiye kıyasla küçük olduğunda ve nesnenin uzun bir ömrü olduğunda kullanımda bir tür bellek sızıntısı olabilir .

Normal çözüm kullanmaktır, new String( s.subString(...))ancak potansiyel / olası bir sonucu saklayan subString(...)ve arayan üzerinde hiçbir kontrolü olmayan bir sınıfınız olduğunda intern(), yapıcıya iletilen String argümanlarını saklamayı düşünebilirsiniz . Bu, potansiyel büyük tamponu serbest bırakır.


İlginç, ama belki de bu uygulamaya bağlıdır.
akostadinov

1
Yukarıda belirtilen potansiyel bellek sızıntısı java 1.8 ve 1.7.06'da (ve daha yeni) gerçekleşmez bkz . Java 1.7.0_06'da yapılan Dize iç gösterimindeki değişiklikler .
eremmel

mikro-optimizasyonların ancak gerektiğinde performans ve / veya bellek profili oluşturulduktan sonra uygulanacağını doğrular. Teşekkür ederim.
akostadinov

2

Dize stajlaması, equals()yöntemin sık sık çağrıldığı durumlarda yararlıdır çünkü equals()yöntem , yöntemin başında nesnelerin aynı olup olmadığını görmek için hızlı bir denetim yapar.

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

Bu genellikle Collectionbaşka bir kodda arama yaparken dize eşitliği denetimleri de yapabilir.

Staj yapmanın bir maliyeti olsa da, bazı kodların mikro karşılaştırmasını yaptım ve staj işleminin çalışma süresini 10 kat artırdığını buldum.

Staj yapmak için en iyi yer genellikle kodun dışında saklanan anahtarları okurken koddaki dizeler otomatik olarak stajyedir. Bu, normalde ilk kullanıcı cezasını önlemek için başvurunuzun başlangıç ​​aşamalarında olur.

Bunun yapılabileceği başka bir yer, anahtar aramaları yapmak için kullanılabilecek kullanıcı girişini işlemektir. Bu normalde istek işlemcinizde olur, interned dizelerin geçirilmesi gerektiğini unutmayın.

Bunun dışında, kodun geri kalanında staj yapmanın pek bir anlamı yoktur, çünkü genellikle herhangi bir fayda sağlamaz.


1

Ben bakım güçlük değmez olmak için oy.

Çoğu zaman, kodlar alt dizelerle çok fazla çalışmazsa, gerek kalmayacak ve performans faydası olmayacaktır. Bu durumda String sınıfı, belleği kaydetmek için orijinal dizeyi artı bir ofseti kullanır. Kodunuz çok fazla alt dize kullanıyorsa, bellek gereksinimlerinizin patlamasına neden olacağından şüpheleniyorum.


1

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

iddia String.equals()kullanım "=="karşılaştırma Stringgöre, önce nesneleri

http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html

Dizelerin uzunluklarını ve ardından içerikleri karşılaştırır.

(Bu arada, bir satış kataloğundaki ürün kodu dizeleri aynı uzunlukta olmakla yükümlüdür - BIC0417 bir bisikletçinin güvenlik kaskı, TIG0003 canlı bir yetişkin erkek kaplan - muhtemelen bunlardan birini sipariş etmek için her türlü lisansa ihtiyacınız var. belki aynı zamanda bir kask sipariş etsen iyi olur.)

Bu nedenle, Dizelerinizi intern()sürümleriyle değiştirmekten yarar görüyorsunuz, ancak equals()programlamanız için "==" kullanmadan güvenlik ve okunabilirlik ve standart uyumluluk elde edersiniz. Ve söyleyeceğim şeylerin çoğu, eğer doğruysa, doğru olmasına bağlıdır.

Ama String.equals()kullanmadan önce onu başka bir nesne değil, bir String geçirdiğinizi test ediyor "=="mu? Söylemek için nitelikli değilim, ama tahmin ediyorum, çünkü ezici bir çoğunlukla bu tür equals()işlemler String to String olacak, bu yüzden test neredeyse her zaman geçti. Gerçekten de, "==" için öncelik vermek, Dizeyi String.equals()sık sık aynı gerçek nesneyle karşılaştırdığınıza dair bir güven anlamına gelir.

Umarım kimse aşağıdaki satırların "yanlış" sonucunu üretmesine şaşırmaz:

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

Değiştirmek Ama eğer ikarşı i.toString()ikinci satırında, tabii ki var true.

Stajyerlikten faydalanmayı ümit edebileceğiniz yerler Setve Maptabii ki. İnterned dizeleri kendi hashcodes önbellek var umarım ... Bence bu bir zorunluluktur. Ve umarım bana sadece bir milyon dolar kazanabilecek bir fikir vermedim. :-)

Belleğe gelince, Dizelerinizin hacmi büyükse veya program kodunuz tarafından kullanılan belleğin çok küçük olmasını istiyorsanız, bunun önemli bir sınır olduğu da açıktır. -Distinct- Dizeleri hacminiz çok büyükse, bunları yönetmek için ayrılmış veritabanı program kodu ve ayrı bir veritabanı sunucusu kullanmayı düşünmenin zamanı gelmiş olabilir. Aynı şekilde, Dizelerini hiç saklamamasını sağlayarak küçük bir programı (aynı anda 10000 örnekte çalışması gerekir) geliştirebilirseniz.

Yeni bir Dize oluşturmak ve ardından intern()yerine koymak için hemen atmak çok zordur , ancak yinelenen Dize'yi tutmak dışında net bir alternatif yoktur. Yani gerçekten yürütme maliyeti, stajyer havuzunda dizenizi aramak ve daha sonra çöp toplayıcının orijinali atmasına izin vermektir. Ve eğer bir dize değişmezse, zaten zaten stajyer olarak gelir.

intern()Bazı String ve onların nesne başvuruları intern()havuzda zaten var olup olmadığını belirlemek için kötü amaçlı program kodu tarafından kötüye kullanıp kullanamayacağını merak ediyorum ve bu nedenle bilinmesi gereken Java oturumunda başka bir yerde var. Ama bu sadece program kodu zaten güvenilir bir şekilde kullanıldığında mümkün olurdu, sanırım. Yine de, ATM PIN numaralarınızı saklamak ve hatırlamak için programınıza eklediğiniz üçüncü taraf kütüphaneleri hakkında düşünmeniz gereken bir şey!


0

Stajyer kullanmanın gerçek nedeni yukarıdakiler değildir. Bellek yetersiz hatası aldıktan sonra kullanabilirsiniz. Tipik bir programdaki dizenin çoğu, diğer büyük dizenin String.substring () yöntemidir [100K xml dosyasından bir kullanıcı adı almayı düşünün. Java uygulaması, alt dize orijinal dize ve bu büyük dize start + end bir referans tutar olmasıdır. (Arkasındaki düşünce aynı büyük ipin yeniden kullanılmasıdır)

Sadece 1000 kısa isim kaydettiğiniz 1000 büyük dosyadan sonra, tüm 1000 dosyayı hafızada tutacaksınız! Çözüm: bu senaryoda sadece smallsubstring.intern () kullanın


Gerekirse neden sadece alt dizeden yeni bir dize oluşturmuyorsunuz?
Thorbjørn Ravn Andersen

0

Bellek kaydetmek için stajyer kullanıyorum, bellekte String veri büyük miktarda tutun ve intern () kullanmak için büyük miktarda bellek kaydetme taşıma. Ne yazık ki daha az bellek kullansa da kullandığı bellek Heap yerine PermGen belleğinde saklanıyor ve müşterilere bu tip bir bellek tahsisinin nasıl arttırılacağını açıklamak zor.

Bellek tüketimini azaltmak için intern () 'e bir alternatif var mı, (== ve performans avantajlarına eşittir benim için bir mendil değil)


0

Kabul edelim: ana kullanım senaryosu, bir veri akışını (bir giriş akışı üzerinden veya bir JDBC ResultSet'ten) okuduğunuzda ve her yerde tekrarlanan sayısız küçük Dizgiler olduğunda.

Aşağıda, Dizeleri ve diğer değişmez öğeleri içselleştirmek için ne tür bir mekanizma kullanmak istediğinizi kontrol eden küçük bir püf noktası ve örnek bir uygulama:

/**
 * 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();
    }
}

Akışları veya ResultSets alanlarını okurken bunu sık sık kullanıyorum. Not: LRUCachetabanlı basit bir önbellektir LinkedHashMap<K,V>. retrieve()Tüm önbellek özledikleri için kullanıcı tarafından sağlanan yöntemi otomatik olarak çağırır .

Bunu kullanmanın yolu LRUInternalizer, okumanızdan (veya okumanızdan) önce bir tane oluşturmak , onu Dizeleri ve diğer küçük sabit nesneleri içselleştirmek, sonra serbest bırakmaktır. Örneğin:

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

0

Ben ilişkili isimlere bağlantı yaklaşık 36000 kodlarının içeriğini önbellek için kullanıyorum. Kodların birçoğu aynı dizeyi gösterdiğinden önbellekteki dizeleri stajyerim.

Önbelleğime dizeleri yerleştirerek, aynı dizeye işaret eden kodların aslında aynı belleği gösterdiğinden emin olurum, böylece RAM alanından tasarruf edersiniz.

İnterned dizeleri aslında çöp toplanmış olsaydı, benim için hiç işe yaramazdı. Bu temelde staj yapma amacını olumsuz yönde etkileyecektir. Benimki çöp toplanmayacak çünkü önbellekteki her bir dizeye referans veriyorum.


Hayır, belirli bir zamanda bellekte olan tüm interned eşit dizeler yine de tek bir nesne olacaktır. Çöp toplanmadan önce bellekteki eşit dizgiden farklı bir nesne olacaktır. Ancak bu sorun değil çünkü eski dize artık orada değil.
bdruemen

0

Bir dizgeyi staj yapmanın maliyeti, tek bir dizede kaydedilen süreden çok daha fazladır. Eşittir (B) karşılaştırması. Yalnızca aynı değişmeyen dize değişkenlerini tekrar tekrar kullandığınızda kullanın (performans nedenleriyle). Örneğin, aynı dize alanına anahtarlanmış bazı haritaları güncellemek için düzenli bir dizeler listesi üzerinde düzenli olarak yinelerseniz, güzel bir tasarruf elde edebilirsiniz.

Kodunuzun belirli bölümlerini en iyi duruma getirirken performansı değiştirmek için dize stajyerliği kullanmanızı öneririm.

Ayrıca String'in değişmez olduğunu ve aptalca hata yapmadığını unutmayın.

String a = SOME_RANDOM_VALUE
a.intern()

yapmayı unutma

String a = SOME_RANDOM_VALUE.intern()

0

Eğer çöp toplanan String.intern için sınırsız bir yedek arıyorsanız, aşağıdaki benim için iyi çalışıyor.

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;
    }
}

Tabii ki, kaç farklı dizenin olacağını kabaca tahmin edebiliyorsanız, -XX: StringTableSize = highEnoughValue ile String.intern () öğesini kullanın .


SoftRef daha fazla anlam ifade eder.
vach

@vach WeakReference kullanarak (SoftReference yerine) bellek daha önce boşaltılır, böylece diğer ayırmalar daha hızlı olabilir. Uygulamanın başka ne yaptığına bağlıdır, her ikisi de mantıklı olabilir.
bdruemen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.