2016-07-15 13 views
19

Jestem świadomy, jeśli uczynićW jaki sposób JVM ponownie wykorzystuje internowane ciągi łańcuchowe?

for (condition) { 
    String s = "hi there"; 
} 

Tylko jeden String instancja jest tworzona we wszystkich iteracji, w przeciwieństwie String s = new String("hi there"); że stworzy nową instancję w każdej iteracji.

Ale czytając Effective Java z Joshua Bloch: Rozdział 2 pkt 5 (strona 20) stwierdza:

Ponadto zagwarantowane jest, że obiekt będzie ponownie wykorzystane przez inny kod uruchomiony w tym samym maszyna wirtualna, która zdarza się zawiera ten sam ciąg literowy [JLS, 3.10.5].

AFAIK, że nie mówi dzieje być ten sam ciąg dosłowne, mówi zawiera.

Czytanie [JLS, 3.10.5] nie może znaleźć dokładnego odniesienia do tego i mam wątpliwości.

Podając ten fragment:

String s1 = "hi "; 
String s2 = "there"; 
String s3 = "hi there"; 

Ile przypadki są tworzone?

  • 3 wystąpienia (a więc wyrażenie nie jest do końca dokładne).
  • dwie instancje s1 i s2 (wtedy s3 powstaje ponowne s1 i s2 odniesienia)
+1

Prawdopodobnie oznacza "maszyna wirtualna zawiera ..", a nie ciąg zawiera inny ciąg –

+1

Nie jestem pewien, więc komentarz zamiast odpowiedzi. Ale myślę, że "zawierają" jest częściowo błędne, a twój przykład rzeczywiście daje trzy przykłady. – glglgl

+0

@glglgl faktycznie jest tym, co * moja logika * mówi, ale może być JVM wystarczająco inteligentny, aby utworzyć 's3' jako odniesienie do' s1' + 's2' ?? –

Odpowiedz

17

JLS nie ponosi żadnej ponowne podrzędnych łańcuchów ogóle. "Zawieraj" tutaj oznacza po prostu, że klasa klasy wymienia dokładnie ten sam ciąg literału. Jest to , a nie użyte w sensie "podciągu".

+2

W szczególności _ "jakikolwiek inny kod [..], który zawiera ** ten sam łańcuch literalny **" _ (podkreślenie moje) –

+1

kiedy mówisz * nie gwarantuje ponownego wykorzystania pod-strun * oznacza, że ​​czasami może się zdarzyć? –

+3

@JordiCastilla: Nie sądzę, aby jakikolwiek VM używał ponownie podłańcuchów, ale jest to możliwe (a poprzednie iteracje OpenJDK na przykład czasami współużytkowały bazowy char [], gdy dwa ciągi były podciągami od siebie nawzajem). Zauważ, że możesz * nadal * obserwować oddzielne instancje "String" i nie ma publicznego interfejsu API do wykrycia, czy tak się dzieje (tj. Nie byłbyś w stanie stwierdzić bez oszustwa do namysłu). –

3

Każdy plik klasy zawiera listę wszystkich literałów łańcuchowych lub innych stałych używanych w tej klasie (z wyjątkiem małych stałych numerycznych, które są osadzone w strumieniu instrukcji). Jeśli pozycja 19 na liście jest literałem literowym "Freddy", a zmienna lokalna Fred ma indeks 6, to kod bajtowy wygenerowany dla Fred="Freddy"; prawdopodobnie byłby ldc 19/astore 6.

Po załadowaniu klasy system utworzy tabelę ze wszystkimi stałymi i - dla obiektów odniesienia - obiekty zidentyfikowane w ten sposób. Jeśli wiadomo, że nie istnieje żadne wystąpienie ciągu literowego, system doda go do tabeli interningowej i zapisze do niego odwołanie. Podczas generowania kodu maszynowego, ldc 19 zostanie zastąpiony instrukcją, aby załadować odpowiednie odniesienie.

Ważne jest to, że w momencie jakiegokolwiek kodu w klasie tras, obiekty zostały stworzone dla wszystkich napisowych w nim, więc stwierdzenie jak Fred="Freddy"; będzie jedynie przechowywać odniesienie do już istniejącej String obiektu zawierającego Freddy , zamiast tworzyć nowy obiekt String.

2

Jeśli s3 ponownego wykorzystania s1 i s2 przypadków, a s3 nie być fizycznie przedstawiony w postaci ciągłej matrycy znaków, ale raczej być złożona String z String s obiektów.

Wyobraźmy sobie teraz wpływ wydajności na dostęp do pojedynczych znaków w takim łańcuchu - dostęp oparty na indeksie faktycznie wymagałby porównania wartości indeksu z rozmiarem pierwszego ciągu, a następnie obliczenia przesunięcia, które stałoby się indeksem dla drugiego ciągu znaków itp. .

Właściwie przeciwnie mogłoby mieć sens: tylko jeden bazowego sekwencja char może być przeznaczona na "hi there" (s3) i s1 i s2 może po prostu przechowywać ich długości i adresy pierwszego znaku w ramach tego łańcucha. Zakładam jednak, że dla jvm byłoby to skomplikowane i kosztowne zadanie zidentyfikowania "możliwych do sfałszowania" kandydatów, a koszt przeważyłby nad potencjalną korzyścią.

+1

Cóż, przed wersją 7 Java 'substring'-method była kiedyś implementowana w taki sposób, że zwróciła String, który był poparty oryginalną tablicą znaków String, ale nawet ta została usunięta, ponieważ spowodowała więcej szkód niż dobra (duże teksty mogły być utrzymywana przy życiu przez utrzymywanie odniesienia do jakiegoś niewielkiego podłańcucha, na przykład) – Hulk

+1

@Hulk: To była [zmiana w Java7update6] (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4513622). To nie jest tylko kwestia gc; wymaga, aby każdy ciąg zawierał pola "offset" i "długość" wyłącznie w celu wykonania pojedynczej operacji, 'substring'. Co więcej, funkcja deduplikacji łańcuchów ostatnich JVM korzysta z uproszczonego układu obiektów, jako że pojedynczy 'cas' w polu' value' jest wystarczający. – Holger

Powiązane problemy