Dlaczego dodawanie "" do łańcucha zapisuje pamięć?

Użyłem zmiennej z dużą ilością danych, powiedzmy String data. Chciałem użyć małej 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 objects smallpart zapamiętało wszystkie dane z 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 bardzo mało pamięci!

Jak to możliwe? Can ktoś to wyjaśni? Myślę, że to.smallpart nadal odwoływał się do danych, ale dlaczego?

Aktualizacja: Jak mogę wyczyścić duży ciąg? Will data = new String ( data.substring (0,100)

Author: hsmit, 2010-01-27

9 answers

Wykonując następujące czynności:

data.substring(x, y) + ""

Tworzy nowy (mniejszy) obiekt String i wyrzuca odniesienie do łańcucha utworzonego przez substring (), umożliwiając tym samym zbieranie śmieci.

Ważną rzeczą do zrealizowania jest to, że substring() daje okno na istniejący Łańcuch znaków - a raczej tablicę znaków leżącą pod oryginalnym łańcuchem. W związku z tym pochłonie tę samą pamięć co oryginalny ciąg znaków. Może to być korzystne w niektórych okolicznościach, ale problematyczne, jeśli chcesz uzyskać podłańcuch i pozbyć się oryginalnego ciągu znaków(jak się dowiedziałeś).

Spójrz na metodę substring () w JDK String source, aby uzyskać więcej informacji.

EDIT: aby odpowiedzieć na dodatkowe pytanie, zbudowanie nowego ciągu znaków z podłańcucha zmniejszy zużycie pamięci, pod warunkiem, że usuniesz wszelkie odniesienia do oryginalnego łańcucha.

Uwaga (styczeń 2013). Powyższe zachowanie zmieniło się w Javie 7u6. Wzór masy ciała jest nie jest już używany i substring() będzie działał zgodnie z oczekiwaniami.

 158
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-06 14:23:02

Jeśli spojrzysz na Źródło substring(int, int), zobaczysz, że zwraca:

new String(offset + beginIndex, endIndex - beginIndex, value);

Gdzie value jest oryginalnym char[]. Więc otrzymujesz nowy ciąg znaków, ale z tym samym char[].

Kiedy to zrobisz, data.substring() + "", otrzymujesz nowy ciąg z nowym char[].

Właściwie, Twój przypadek użycia jest jedyną sytuacją, w której powinieneś użyć konstruktora String(String):

String tiny = new String(huge.substring(12,18));
 27
Author: Pascal Thivent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-27 15:03:54

Kiedy używasz substring, nie tworzy on nowego ciągu znaków. Nadal odnosi się do oryginalnego ciągu, z ograniczeniem offsetu 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).

 17
Author: Chris Jester-Young,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-27 14:55:47
Chyba tak.smallpart odwoływanie się do danych, ale dlaczego?

Ponieważ ciągi Javy składają się z tablicy znaków, przesunięcia początkowego i długości (oraz buforowanego hashCode). Niektóre operacje łańcuchowe, takie jak substring(), tworzą nowy obiekt Łańcuchowy, który dzieli oryginalną tablicę znaków i po prostu ma inne pola offset i / lub długość. Działa to, ponieważ tablica znaków łańcucha znaków nigdy nie jest modyfikowana po jego utworzeniu.

To może zapisać pamięć, gdy wiele podciągów odwołuje się do do tego samego podstawowego ciągu bez powielania nakładających się części. Jak zauważyłeś, w niektórych sytuacjach może to uchronić dane, które nie są już potrzebne, przed zbieraniem śmieci.

"poprawnym" sposobem naprawienia tego jest konstruktor new String(String), czyli

this.smallpart = new String(data.substring(12,18));

BTW, ogólnie najlepszym rozwiązaniem byłoby, aby uniknąć bardzo dużych ciągów w pierwszej kolejności, i przetwarzania każdego wejścia w mniejszych kawałkach, aa kilka KB na raz.

 5
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-27 15:05:35

W Javie ciągi znaków są obiektami imutowalnymi i po utworzeniu łańcucha znaków pozostaje on w pamięci, dopóki nie zostanie wyczyszczony przez kolektor śmieci (a to czyszczenie nie jest czymś, co można wziąć za pewnik).

Wywołując metodę substring, Java nie tworzy prawdziwie nowego ciągu znaków, lecz jedynie przechowuje zakres znaków wewnątrz oryginalnego ciągu.

Więc po utworzeniu nowego ciągu z tym kodem:

this.smallpart = data.substring(12, 18) + ""; 

Utworzyłeś nowy łańcuch, gdy połączyłeś wynik z pustym łańcuchem. Dlatego.

 5
Author: Kico Lobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-26 09:23:18

Zgodnie z dokumentacją jwz w 1997:

Jeśli masz ogromny łańcuch, wyciągnij z niego łańcuch, przytrzymaj go i pozwól dłuższemu łańcuchowi stać się śmieciem (innymi słowy, łańcuch ma dłuższy czas życia).

 3
Author: Ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-27 15:13:43

Podsumowując, jeśli tworzysz wiele podciągów z małej liczby dużych łańcuchów, użyj

   String subtring = string.substring(5,23)

Ponieważ używasz spacji tylko do przechowywania dużych strun, ale jeśli wydobywasz tylko garść małych strun, z utraconych dużych strun, to

   String substring = new String(string.substring(5,23));

Zmniejszy zużycie pamięci, ponieważ duże struny mogą być odzyskane, gdy nie są już potrzebne.

Że wywołujesz {[2] } jest pomocnym przypomnieniem, że naprawdę otrzymujesz nowy ciąg znaków, a nie odniesienie do oryginał.

 2
Author: mdma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-29 01:32:31

Po pierwsze, wywołanie java.lang.String.substring tworzy nowe okno na oryginale String przy użyciu offsetu i długości zamiast kopiowania znaczącej części bazowej tablicy.

Jeśli przyjrzymy się bliżej metodzie substring zauważymy konstruktor ciągu wywołanie String(int, int, char[]) i przekazanie go w całości char[], który reprezentuje string. Oznacza to, że substrat zajmie tyle pamięci co oryginalny string.

Ok, ale dlaczego + "" powoduje zapotrzebowanie na mniej pamięci niż bez niej??

Wykonywanie + na strings jest zaimplementowane poprzez wywołanie metody StringBuilder.append. Spójrz na implementację tej metody w klasie AbstractStringBuilder powie nam, że w końcu robi {[11] } z częścią, której naprawdę potrzebujemy (substring).

Jakieś inne obejście??

this.smallpart = new String(data.substring(12,18));
this.smallpart = data.substring(12,18).intern();
 2
Author: laika,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-02 14:34:39

Dołączenie "" do łańcucha spowoduje Czasami zapisanie pamięci.

Powiedzmy, że mam ogromny ciąg zawierający całą książkę, milion znaków.

Następnie tworzę 20 ciągów zawierających rozdziały książki jako podłańcuchy.

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 utworzyć 100,000 ciągów zawierających wszystkie słowa.

I still only use 1,000,000 postaci. 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 wyciągniesz tylko jedno słowo z całej książki, a cała książka może być zbierana, ale nie dlatego, że 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ń, aby utworzyć podłańcuch. Sposób działania Javy lub praca unika kopiowania miliona znaków za każdym razem. Jest kompromis i dobrze, jeśli wiesz, jakie są kompromisy.

 0
Author: gnasher729,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-27 19:43:39