Co to jest String Interning w Javie, kiedy powinienem go używać i dlaczego ?
java
string
string-interning
saplingPro
źródło
źródło
String a = new String("abc");
String b = new String("abc");
toa.intern() == b.intern()
String.intern()
zależy to od tegoClassLoader
, czy różne moduły ładujące klasy tworzą „różne”String
, powodując różneintern
?Odpowiedzi:
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern ()
Zasadniczo wykonanie String.intern () na szeregu ciągów zapewni, że wszystkie ciągi mające tę samą zawartość będą miały tę samą pamięć. Więc jeśli masz listę nazwisk, w których „John” pojawia się 1000 razy, internując masz pewność, że tylko jeden „John” jest rzeczywiście przydzielony do pamięci.
Może to być przydatne w celu zmniejszenia wymagań dotyczących pamięci twojego programu. Ale pamiętaj, że pamięć podręczna jest utrzymywana przez JVM w stałej puli pamięci, która zwykle ma ograniczony rozmiar w porównaniu do sterty, więc nie powinieneś używać intern, jeśli nie masz zbyt wielu zduplikowanych wartości.
Więcej informacji na temat ograniczeń pamięci związanych z używaniem intern ()
- Od: http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html
Od JDK 7 (mam na myśli w HotSpot) coś się zmieniło.
- Z funkcji i ulepszeń Java SE 7
Aktualizacja: Wewnętrzne ciągi są przechowywane w stercie głównym od Java 7 i późniejszych. http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes
źródło
char[]
zamiastString
wrażliwego tekstu i wyzerować go, gdy tylko nie będzie już potrzebny.Są pewne „chwytliwe wywiady”, na przykład dlaczego jesteś równy! jeśli wykonasz poniższy fragment kodu.
Jeśli chcesz porównać ciągi, których powinieneś użyć
equals()
. Powyższe spowoduje wydrukowanie równe, ponieważtestString
jest już internowane przez kompilator. Możesz internować łańcuchy samodzielnie za pomocą metody intern, jak pokazano w poprzednich odpowiedziach ....źródło
equals
metody. Możesz dodaćnew String()
porównanie, aby wyraźniej pokazać rozróżnienie.JLS
JLS 7 3.10.5 definiuje to i podaje praktyczny przykład:
JVMS
JVMS 7 5.1 mówi , że internowanie jest realizowane magicznie i skutecznie za pomocą dedykowanej
CONSTANT_String_info
struktury (w przeciwieństwie do większości innych obiektów, które mają bardziej ogólne reprezentacje):Kod bajtowy
Dekompilujmy kod bajtowy OpenJDK 7, aby zobaczyć internowanie w akcji.
Jeśli dekompilujemy:
mamy na stałej puli:
i
main
:Uwaga jak:
0
i3
:ldc #2
ładowana jest ta sama stała (literały)12
: tworzona jest nowa instancja ciągu (z#2
argumentem jako)35
:a
ic
są porównywane jako zwykłe obiekty zif_acmpne
Reprezentacja ciągów ciągłych jest dość magiczna w kodzie bajtowym:
new String
)a powyższy cytat JVMS wydaje się mówić, że ilekroć wskazany Utf8 jest taki sam, ładowane są identyczne instancje
ldc
.Zrobiłem podobne testy dla pól i:
static final String s = "abc"
wskazuje na stałą tabelę poprzez atrybut ConstantValueldc
Wniosek : istnieje bezpośrednia obsługa kodów bajtów dla puli ciągów, a reprezentacja pamięci jest wydajna.
Bonus: porównaj to z pulą liczb całkowitych , która nie ma bezpośredniego wsparcia dla kodu bajtowego (tzn. Nie ma
CONSTANT_String_info
analogu).źródło
Aktualizacja dla Java 8 lub nowszej . W Javie 8 przestrzeń PermGen (Permanent Generation) jest usuwana i zastępowana przez Meta Space. Pamięć puli ciągów jest przenoszona na stertę maszyny JVM.
W porównaniu z Javą 7 wielkość puli ciągów jest zwiększana w stercie. Dlatego masz więcej miejsca na zinternalizowane ciągi, ale masz mniej pamięci dla całej aplikacji.
Jeszcze jedna rzecz, wiesz już, że podczas porównywania 2 (
==
referencji ) obiektów w Javie „ ” służy do porównywania referencji do obiektu, „equals
” służy do porównywania zawartości obiektu.Sprawdźmy ten kod:
Wynik:
value1 == value2
---> prawdavalue1 == value3
---> falsevalue1.equals(value3)
---> prawdavalue1 == value3.intern()
---> prawdaDlatego powinieneś użyć „
equals
” do porównania 2 obiektów String. I tointern()
jest przydatne.źródło
Internowanie ciągów jest techniką optymalizacji przez kompilator. Jeśli masz dwie identyczne literały łańcuchowe w jednej jednostce kompilacji, wówczas wygenerowany kod zapewnia, że istnieje tylko jeden obiekt łańcuchowy utworzony dla wszystkich wystąpień tego literału (znaki ujęte w podwójne cudzysłowy) w zestawie.
Jestem w tle C #, więc mogę wyjaśnić, podając przykład z tego:
wynik następujących porównań:
Uwaga 1 : Obiekty są porównywane przez odniesienie.
Uwaga 2 : typeof (int) .Nazwa jest obliczana metodą refleksyjną, więc nie jest obliczana podczas kompilacji. Tutaj porównuje się je w czasie kompilacji.
Analiza wyników: 1) prawda, ponieważ oba zawierają ten sam literał, a więc wygenerowany kod będzie miał tylko jeden obiekt odwołujący się do „Int32”. Uwaga 1 .
2) prawda, ponieważ sprawdzana jest zawartość obu wartości, która jest taka sama.
3) FAŁSZ, ponieważ str2 i obj nie mają tego samego literału. Patrz uwaga 2 .
źródło
źródło
Z książki Desmukha programisty OCP Java SE 11 znalazłem najłatwiejsze wyjaśnienie dotyczące internowania, które brzmiało następująco: Ponieważ ciągi są obiektami, a ponieważ wszystkie obiekty w Javie są zawsze przechowywane tylko w przestrzeni sterty, wszystkie ciągi są przechowywane w przestrzeni sterty. Jednak Java utrzymuje ciągi utworzone bez użycia nowego słowa kluczowego w specjalnym obszarze przestrzeni sterty, który nazywa się „pulą ciągów”. Java utrzymuje ciągi utworzone przy użyciu nowego słowa kluczowego w zwykłej przestrzeni sterty.
Celem puli ciągów jest utrzymanie zestawu unikatowych ciągów. Za każdym razem, gdy tworzysz nowy ciąg bez użycia nowego słowa kluczowego, Java sprawdza, czy ten sam ciąg już istnieje w puli ciągów. Jeśli tak, Java zwraca odwołanie do tego samego obiektu String, a jeśli nie, Java tworzy nowy obiekt String w puli ciągów i zwraca swoje odwołanie. Na przykład, jeśli użyjesz w kodzie dwa razy łańcucha „cześć”, jak pokazano poniżej, otrzymasz odwołanie do tego samego ciągu. Możemy faktycznie przetestować tę teorię, porównując dwie różne zmienne odniesienia za pomocą operatora == , jak pokazano w następującym kodzie:
== operator sprawdza po prostu, czy dwa odniesienia wskazują na ten sam obiekt, czy nie, i zwraca true, jeśli tak jest. W powyższym kodzie str2 pobiera odwołanie do tego samego obiektu String, który został wcześniej utworzony. Jednak str3 i str4 otrzymują odniesienia do dwóch całkowicie różnych obiektów String. Dlatego str1 == STR2 powraca prawda, ale str1 == str3 i str3 == STR4 return false. W rzeczywistości, kiedy robisz nowy ciąg („hello”); dwa obiekty String są tworzone zamiast jednego, jeśli po raz pierwszy łańcuch „hello” jest używany w dowolnym miejscu w programie - jeden w puli łańcuchów ze względu na użycie łańcucha cytowanego i jeden w regularnej przestrzeni sterty, ponieważ użycia nowego słowa kluczowego.
Pula ciągów jest sposobem Java na oszczędzanie pamięci programu poprzez unikanie tworzenia wielu obiektów String zawierających tę samą wartość. Możliwe jest uzyskanie ciągu z puli ciągów dla ciągu utworzonego przy użyciu nowego słowa kluczowego za pomocą metody intern String. Nazywa się to „internowaniem” obiektów łańcuchowych. Na przykład,
źródło