Почему добавление «” в String сохраняет память?

Я использовал переменную с большим количеством данных в ней, скажем, String data . Я хотел использовать небольшую часть этой строки следующим образом:

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

После нескольких часов отладки (с помощью визуализатора памяти) я обнаружил, что поле объектов smallpart запоминает все данные из data , хотя в нем содержится только подстрока.

Когда я изменил код на:

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

.. проблема решена! Теперь мое приложение использует очень мало памяти!

Как это возможно? Может кто-нибудь объяснить это? Я думаю, что this.smallpart продолжал ссылаться на данные, но почему?

UPDATE: Как я могу очистить большую строку? Будут ли данные = новая строка (data.substring (0,100))?

Выполнение следующих действий:

 data.substring(x, y) + "" 

создает новый (меньший) объект String и отбрасывает ссылку на String, созданную подстрокой (), тем самым обеспечивая сборку мусора.

Важно понять, что substring() дает окно на существующую строку – или, скорее, массив символов, лежащий в основе исходной строки. Следовательно, он будет потреблять ту же память, что и исходная строка. Это может быть выгодно в некоторых случаях, но проблематично, если вы хотите получить подстроку и избавиться от исходной строки (как вы выяснили).

Взгляните на метод substring () в источнике JDK String для получения дополнительной информации.

EDIT: Чтобы ответить на ваш дополнительный вопрос, построение новой строки из подстроки уменьшит потребление памяти, если вы выберете любые ссылки на исходную строку.

ПРИМЕЧАНИЕ (январь 2013 г.). Вышеописанное поведение изменилось в Java 7u6 . Паттерн flyweight больше не используется, и substring() будет работать так, как вы ожидали.

Если вы посмотрите на источник substring(int, int) , вы увидите, что он возвращает:

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

где value – исходный char[] . Таким образом, вы получаете новую строку, но с тем же основным char[] .

Когда вы это сделаете, data.substring() + "" , вы получите новую String с новым базовым char[] .

Фактически, ваш вариант использования – единственная ситуация, когда вы должны использовать конструктор String(String) :

 String tiny = new String(huge.substring(12,18)); 

Когда вы используете substring , она фактически не создает новую строку. Он по-прежнему относится к вашей исходной строке с ограничением смещения и размера.

Итак, чтобы ваша исходная строка была собрана, вам нужно создать новую строку (используя new String или то, что у вас есть).

Я думаю, что this.smallpart продолжал ссылаться на данные, но почему?

Поскольку строки Java состоят из массива символов, смещения начала и длины (и кэшированного hash-кода). Некоторые строковые операции, такие как substring() создают новый объект String, который разделяет массив символов оригинала и просто имеет разные поля смещения и / или длины. Это работает, потому что массив символов String никогда не изменяется после его создания.

Это может сэкономить память, когда многие подстроки ссылаются на одну и ту же базовую строку без дублирования перекрывающихся частей. Как вы заметили, в некоторых ситуациях он может хранить данные, которые больше не нужны, от сбора мусора.

«Правильный» способ исправить это new String(String) , т. Е.

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

Кстати, лучшим лучшим решением было бы избежать того, чтобы в первую очередь было иметь очень большие строки и обрабатывать любой вход в более мелкие куски, всего несколько килобайт за раз.

В строках Java находятся объекты, подлежащие уничтожению, и после создания строки она остается в памяти, пока она не будет очищена мусорным коллектором (и эта очистка не является чем-то, что вы можете считать само собой разумеющимся).

Когда вы вызываете метод подстроки, Java не создает тривиально новую строку, а просто сохраняет ряд символов внутри исходной строки.

Итак, когда вы создали новую строку с этим кодом:

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

вы фактически создали новую строку, когда вы конкатенировали результат с пустой строкой. Вот почему.

Как указано в jwz в 1997 году :

Если у вас есть огромная строка, вытащите из нее подстроку (), держитесь за подстроку и позволяйте более длинной строке становиться мусором (другими словами, подстрока имеет более длительное время жизни) базовые байты огромной строки никогда не идут далеко.

Подводя итог, если вы создаете множество подстрок из небольшого числа больших строк, используйте

  String subtring = string.substring(5,23) 

Поскольку вы используете только пространство для хранения больших строк, но если вы извлекаете только небольшое количество маленьких строк, от потерь больших строк, то

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

Удерживает использование памяти, так как большие строки могут быть восстановлены, когда они больше не нужны.

То, что вы называете new String является полезным напоминанием о том, что вы действительно получаете новую строку, а не ссылку на исходную.

Во-первых, вызов java.lang.String.substring создает новое окно в исходной String с использованием смещения и длины вместо копирования значительной части базового массива.

Если мы более подробно рассмотрим метод substring мы увидим, что конструктор строк вызывает String(int, int, char[]) и передает ему весь char[] который представляет строку . Это означает, что подстрока будет занимать столько же объема памяти, сколько и исходная строка .

Хорошо, но почему + "" приводит к спросу на меньшую память, чем без него?

Выполнение strings + выполняется с помощью вызова метода StringBuilder.append . Посмотрите на реализацию этого метода в classе AbstractStringBuilder который скажет нам, что он, наконец, делает arraycopy с той частью, которая нам просто нужна ( substring ).

Любое другое обходное решение?

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

При добавлении «” к строке иногда сохраняется память.

Скажем, у меня огромная строка, содержащая целую книгу, миллион символов.

Затем я создаю 20 строк, содержащих главы книги, как подстроки.

Затем я создаю 1000 строк, содержащих все абзацы.

Затем я создаю 10 000 строк, содержащих все предложения.

Затем я создаю 100 000 строк, содержащих все слова.

Я все еще использую только 1 000 000 символов. Если вы добавите «» в каждую главу, абзац, предложение и слово, вы используете 5 000 000 символов.

Конечно, это совсем другое, если вы извлекаете только одно слово из всей книги, и вся книга может быть собрана мусором, но не потому, что одно слово содержит ссылку на нее.

И это опять-таки отличается, если у вас есть одна миллионная строка символов и удаляйте вкладки и пробелы с обоих концов, делая 10 вызовов для создания подстроки. Способ работы или работы Java позволяет избежать копирования миллиона символов каждый раз. Есть компромисс, и хорошо, если вы знаете, что такое компромиссы.

  • Ошибка преобразования длинного списка data.frames (~ 1 миллион) в единый файл data.frame с использованием do.call и ldply
  • Что более эффективно: System.arraycopy vs Arrays.copyOf?
  • Как решить медленную Java `SecureRandom`?
  • Производительность Android xml vs java
  • JMeter: Как записывать трафик HTTPS?
  • Заголовок Content-Length по сравнению с закодированным кодированием
  • HttpWebRequest очень медленный!
  • Производительность C ++ по сравнению с Java / C #
  • Механизм отправки сообщения Objective C
  • Сравнение C # и OrderBy
  • Храните PostgreSQL от выбора плохого плана запроса
  • Давайте будем гением компьютера.