Powiedzmy, że użyłem zmiennej z dużą ilością danych String data
. Chciałem użyć niewielkiej części tego ciągu w następujący sposób:
this.smallpart = data.substring(12,18);
Po kilku godzinach debugowania (za pomocą wizualizatora pamięci) dowiedziałem się, że pole obiektów smallpart
zapamiętało wszystkie dane data
, chociaż zawierało tylko podłańcuch.
Kiedy zmieniłem kod na:
this.smallpart = data.substring(12,18)+"";
..problem został rozwiązany! Teraz moja aplikacja zużywa teraz bardzo mało pamięci!
Jak to możliwe? Czy ktoś może to wyjaśnić? Wydaje mi się, że to. Mała część ciągle odnosiła się do danych, ale dlaczego?
AKTUALIZACJA: Jak mogę zatem wyczyścić duży Ciąg? Czy dane = nowy ciąg (data.substring (0,100)) to zrobi?
java
performance
string
memory
hsmit
źródło
źródło
new String(String)
; patrz stackoverflow.com/a/390854/8946 .Odpowiedzi:
Wykonując następujące czynności:
tworzy nowy (mniejszy) obiekt String i odrzuca odwołanie do obiektu String utworzonego przez substring (), umożliwiając w ten sposób wyrzucanie elementów bezużytecznych.
Ważne jest, aby zdawać sobie sprawę, że
substring()
daje okno na istniejący Ciąg - a raczej tablicę znaków leżącą u podstaw oryginalnego Ciągu. Dlatego zajmie tę samą pamięć co oryginalny ciąg. Może to być korzystne w niektórych okolicznościach, ale problematyczne, jeśli chcesz uzyskać podciąg i pozbyć się oryginalnego ciągu (jak się dowiedziałeś).Spójrz na metodę substring () w źródle String JDK, aby uzyskać więcej informacji.
EDYCJA: Aby odpowiedzieć na dodatkowe pytanie, zbudowanie nowego ciągu znaków z podciągu zmniejszy zużycie pamięci, pod warunkiem, że bin będzie odwoływał się do oryginalnego ciągu.
UWAGA (styczeń 2013). Powyższe zachowanie zmieniło się w Javie 7u6 . Wzór flyweight nie jest już używany i
substring()
będzie działał zgodnie z oczekiwaniami.źródło
String(String)
konstruktor (tj. Konstruktor String przyjmujący Ciąg jako dane wejściowe) jest użyteczny:new String(data.substring(x, y))
robi to samo, co dołączanie""
, ale czyni intencję nieco jaśniejszą.value
atrybut oryginalnego łańcucha. Myślę, że właśnie dlatego referencja jest przechowywana.Jeśli spojrzysz na źródło
substring(int, int)
, zobaczysz, że zwraca:gdzie
value
jest oryginałchar[]
. Otrzymujesz nowy ciąg znaków, ale z tym samym instrumentem bazowymchar[]
.Kiedy to zrobisz,
data.substring() + ""
otrzymasz nowy Ciąg z nowym instrumentem bazowymchar[]
.W rzeczywistości twój przypadek użycia jest jedyną sytuacją, w której powinieneś użyć
String(String)
konstruktora:źródło
new String(String)
; patrz stackoverflow.com/a/390854/8946 .Kiedy używasz
substring
, tak naprawdę nie tworzy nowego ciągu. Nadal odnosi się do twojego oryginalnego ciągu, z ograniczeniem przesunięcia i rozmiaru.Tak więc, aby umożliwić zbieranie oryginalnego ciągu, musisz utworzyć nowy ciąg (używając
new String
lub tego, co masz).źródło
Ponieważ łańcuchy Java składają się z tablicy char, przesunięcia początkowego i długości (oraz buforowanego kodu skrótu). Niektóre operacje na łańcuchach, takie jak
substring()
tworzenie nowego obiektu String, który dzieli tablicę znaków oryginału i po prostu ma inne pola przesunięcia i / lub długości. Działa to, ponieważ tablica znaków ciągu nie jest nigdy modyfikowana po utworzeniu.Może to zaoszczędzić pamięć, gdy wiele podciągów odnosi się do tego samego ciągu podstawowego bez replikacji nakładających się części. Jak zauważyłeś, w niektórych sytuacjach może to uniemożliwić zbieranie niepotrzebnych danych.
„Poprawnym” sposobem naprawienia tego jest
new String(String)
konstruktor, tjBTW, ogólnie najlepszym rozwiązaniem byłoby uniknięcie posiadania bardzo dużych Ciągów w pierwszej kolejności i przetwarzanie wszelkich danych wejściowych w mniejszych porcjach, po kilka KB na raz.
źródło
new String(String)
; patrz stackoverflow.com/a/390854/8946 .W Javie ciągi są przypisywalnymi obiektami, a po utworzeniu ciąg pozostaje w pamięci, dopóki nie zostanie wyczyszczony przez moduł wyrzucający elementy bezużyteczne (a tego czyszczenia nie można brać za pewnik).
Po wywołaniu metody substring Java nie tworzy prawdziwie nowego ciągu, ale po prostu przechowuje zakres znaków w oryginalnym ciągu.
Kiedy więc utworzyłeś nowy ciąg z tym kodem:
faktycznie utworzyłeś nowy ciąg, łącząc wynik z pustym ciągiem. Dlatego.
źródło
Jak udokumentował jwz w 1997 roku :
źródło
Podsumowując, jeśli tworzysz wiele podciągów z niewielkiej liczby dużych ciągów, użyj
Ponieważ używasz tylko miejsca do przechowywania dużych ciągów, ale jeśli wyciągasz garść małych ciągów, z utraconych dużych ciągów, to
Zmniejszy zużycie pamięci, ponieważ duże łańcuchy można odzyskać, gdy nie są już potrzebne.
To, co wywołujesz,
new String
jest przydatnym przypomnieniem, że naprawdę otrzymujesz nowy ciąg, a nie odniesienie do oryginalnego.źródło
new String(String)
; patrz stackoverflow.com/a/390854/8946 .Po pierwsze, wywołanie
java.lang.String.substring
tworzy nowe okno oryginałuString
z wykorzystaniem przesunięcia i długości zamiast kopiowania znacznej części podstawowej tablicy.Jeśli przyjrzymy się bliżej
substring
metodzie, zauważymy wywołanie konstruktora łańcuchaString(int, int, char[])
i przekazanie go w całości,char[]
który reprezentuje łańcuch . Oznacza to, że podciąg zajmie tyle samo pamięci, co oryginalny ciąg .Ok, ale dlaczego
+ ""
powoduje zapotrzebowanie na mniej pamięci niż bez niej?Wykonanie
+
włączeniastrings
jest realizowane zaStringBuilder.append
pomocą wywołania metody. Spójrz na implementację tej metody wAbstractStringBuilder
klasie powie nam, że w końcu robi toarraycopy
z częścią, której naprawdę potrzebujemy (substring
).Wszelkie inne obejście?
źródło
Czasami dołącza się ciąg „” oszczędzać pamięć.
Powiedzmy, że mam ogromny ciąg zawierający całą książkę, milion znaków.
Następnie tworzę 20 ciągów znaków zawierających rozdziały książki jako podciągi.
Następnie tworzę 1000 ciągów zawierających wszystkie akapity.
Następnie tworzę 10 000 ciągów zawierających wszystkie zdania.
Następnie tworzę 100 000 ciągów zawierających wszystkie słowa.
Nadal używam tylko 1 000 000 znaków. Jeśli dodasz „” do każdego rozdziału, akapitu, zdania i słowa, użyjesz 5 000 000 znaków.
Oczywiście jest zupełnie inaczej, jeśli wyodrębnisz tylko jedno słowo z całej książki, a cała książka może zostać wyrzucona do śmieci, ale nie dlatego, że to jedno słowo zawiera odniesienie do niej.
I znowu jest inaczej, jeśli masz milion znaków i usuwasz tabulatory i spacje na obu końcach, wykonując powiedzmy 10 wywołań w celu utworzenia podłańcucha. Sposób, w jaki działa lub działa Java, pozwala uniknąć kopiowania miliona znaków za każdym razem. Jest kompromis i dobrze, jeśli wiesz, jakie są kompromisy.
źródło