2010-05-26 11 views
16

Jeśli masz dwa wystąpienia ciągu i są one równe, w Javie będą współużytkować tę samą pamięć. W jaki sposób jest to realizowane pod maską?W jaki sposób java implementuje wzór maski pod ciągiem pod maską?

EDYCJA: Moja aplikacja używa dużej liczby obiektów String, z których wiele jest identycznych. Jaki jest najlepszy sposób korzystania ze stałej puli Java String, aby uniknąć tworzenia niestandardowej implementacji flyweight?

Odpowiedz

6

Sprawdź kod źródłowy java.lang.String (źródło całego java api jest częścią JDK).

Podsumowując: Ciąg zawija podsekcję char[]. Ten podkład char[] nigdy nie jest modyfikowany. Osiąga się to przez nie przeciekanie ani przechwytywanie tego char[] poza klasą String. Jednak kilka Strings może współdzielić ten sam char[] (patrz Implementacja String.substring).

Istnieje również mechanizm interningu, jak wyjaśniono w innych odpowiedziach.

+0

Fakt, że 'String.substring' nie przydziela nowego' char [] 'nie jest już prawdą. Zobacz [tę odpowiedź] (http://stackoverflow.com/a/14161077/4464702). – RAnders00

+0

To prawda.Łańcuch nie implementuje już wzorca masy, ponieważ udostępnianie referencji jest teraz uważane za droższe niż zmniejszenie "wagi" łańcuchów znaków, częściowo dlatego, że maszyny JVM zostały ulepszone w celu przydzielania obiektów na stosie, jeśli analiza ucieczki udowodni, że obiekt nie może przeżyć current stack frame - optymalizacja polegająca na tym, że obiekt "char []" nie jest współdzielony. – meriton

4

To nie jest konieczne. Przykład:

String s1 = "hello"; 
String s2 = "hello"; 
System.out.println(s1 == s2); // true 

ale:

String s1 = new String("hello"); 
String s2 = new String("hello"); 
System.out.println(s1 == s2); // false 

Teraz druga forma nie jest zalecane. Niektórzy (w tym ja) uważają, że String nie powinien mieć nawet publicznego konstruktora. Lepsza wersja powyższego byłoby:

String s1 = new String("hello").intern(); 
String s2 = new String("hello").intern(); 
System.out.println(s1 == s2); // true 

Oczywiście nie trzeba to zrobić dla stałej String. To jest przykładowe.

Ważną rzeczą jest to, że jeśli uchwalił String lub dostać jeden z funkcji nie można polegać na String będąc kanoniczną. kanoniczneObject spełnia równość:

a.equals(b) == b.equals(a) == (a == b) 

dla non- null przypadkach a, b, danego Class.

+2

Słowo ostrzeżenia dotyczące interningu polega na tym, że używa on pamięci PermGen, co może skutkować bardzo nieprzyjemnym "OutOfMemoryError". Jeśli konieczne jest łączenie ciągów, niestandardowa pula jest często lepszym wyborem: http://hype-free.blogspot.com/2010/03/stringintern-there-are-better-ways.html – gustafc

+0

Od wersji Java 7, internowane ciągi nie są już w PermGen. Se [ta odpowiedź] (http://stackoverflow.com/a/16298053/4464702). @gustafc – RAnders00

6

Literały łańcuchowe są internowane w Javie, więc istnieje naprawdę tylko jeden obiekt typu String z wieloma odniesieniami (gdy są one równe, co nie zawsze ma miejsce). Więcej informacji można znaleźć w artykule java.net pod numerem All about intern().

Istnieje również dobry przykład/wyjaśnienie w sekcji 3.10.5 String Literals w JLS, który mówi o tym, kiedy struny są internowane i kiedy będą odrębne.

12

Jeśli masz dwie instancje String i są równe, w Javie będą dzielić tej samej pamięci

To rzeczywiście nie jest w 100% prawdziwe.

This blog post is a decent explanation dlaczego tak jest i co to jest Ciąg stała pula.

+0

+1: Ta odpowiedź i odpowiedź Billa jaszczurki są w rzeczywistości tymi, które naprawdę zajmują się tym pytaniem. – haylem

3

Aby odpowiedzieć na zredagowane pytanie, maszyny Sun JVM mają opcję -XX:+StringCache, która w mojej obserwacji może znacząco zmniejszyć rozmiar pamięci ciężkiej aplikacji String.

W przeciwnym razie istnieje możliwość interweniowania w Strings, ale byłbym ostrożny. Ciągi, które są bardzo duże i nie są już przywoływane, będą nadal używać pamięci przez cały czas trwania JVM.

Edit (w odpowiedzi na komentarz): Po raz pierwszy dowiedziałem się o możliwości StringCache z here:

-XX: + StringCache Włącza buforowanie powszechnie przydzielonych strun.

Tom Hawtin opisuje pewien typ buforowania, aby poprawić niektóre testy porównawcze. Moją obserwacją, kiedy umieściłem to na IDEA było to, że ślad pamięci (po pełnym zbiorze śmieci) zszedł na dół, nie mając go. Nie jest to udokumentowany parametr i może w istocie dotyczyć optymalizacji niektórych benchmarków. Moją obserwacją jest to, że pomogło, ale nie zbudowałem na nim ważnego systemu.

+0

Próbowałem znaleźć więcej informacji na -XX: + StringCache, ale bezskutecznie. Gdzie mogę przeczytać więcej na temat tej opcji i jak może ona zmniejszyć ślad pamięci? Czy masz więcej informacji na temat tego, co ta opcja ma do VM? – Dan

1

dwie rzeczy uważać:

  1. Nie używaj new String("abc") konstruktora, wystarczy użyć dosłowne "abc".
  2. Naucz się używać metody intern() w klasie String. Zwłaszcza przy łączeniu łańcuchów razem lub przy konwersji tablicy char/tablica bajtów/etc do String.

intern() zwraca zawsze ciągi, które są połączone.

0

Jeśli identyczne ciągi pochodzą ze stałego zestawu możliwych wartości, to tutaj należy wybrać wyliczanie typu bezpiecznego. Nie tylko zmniejszy liczbę twoich Stringów, ale także sprawi, że aplikacja będzie bardziej stabilna. Twoja cała aplikacja będzie znała ten Ciąg z przypisaną do niego semantyką, może nawet z pewnymi wygodnymi metodami.

Moje ulubione optymalizacje to zawsze te, których można obronić, czyniąc kod lepszym, a nie tylko szybszym. I 9 razy na 10, zamiana łańcucha na konkretny typ prowadzi do bardziej poprawnego i samokodującego kodu.