Я использовал переменную с большим количеством данных в нем, сказать String data
. Я хотел использовать небольшую часть этой строки следующим образом:
this.smallpart = data.substring(12,18);
После нескольких часов отладки (с памятью visualizer) я узнал что поле объектов smallpart
помнивший все данные из data
, хотя это только содержало подстроку.
Когда я изменил код в:
this.smallpart = data.substring(12,18)+"";
.. проблема была решена! Теперь мое приложение использует очень мало памяти теперь!
Как это возможно? Кто-либо может объяснить это? Я думаю, что this.smallpart продолжал ссылаться к данным, но почему?
ОБНОВЛЕНИЕ: Как я могу очистить большую Строку тогда? Будет данные =, новая Строка (data.substring (0,100)) делает вещь?
Выполнение следующих действий:
data.substring(x, y) + ""
создает новый (меньший) объект String и отбрасывает ссылку на созданную строку с помощью substring (), тем самым разрешая сборку мусора this.
Важно понимать, что substring ()
дает окно на существующую String - или, скорее, на массив символов, лежащий в основе исходной String. Следовательно, он будет использовать ту же память, что и исходная строка. В некоторых случаях это может быть выгодно, но проблематично, если вы хотите получить подстроку и избавиться от исходной строки (как вы выяснили).
Для получения дополнительной информации ознакомьтесь с методом substring () в источнике строки JDK.
РЕДАКТИРОВАТЬ: Чтобы ответить на ваш дополнительный вопрос, построение новой строки из подстроки снизит потребление памяти, при условии , что вы сохраняете любые ссылки на исходную строку.
ПРИМЕЧАНИЕ (январь 2013 г.). Вышеупомянутое поведение было изменено в Java 7u6 . Шаблон flyweight больше не используется, и substring ()
будет работать, как и следовало ожидать.
При использовании подстроки
, он на самом деле не создает новую строку. Он по-прежнему относится к вашей исходной строке, с ограничением смещения и размера.
Итак, чтобы позволить собирать исходную строку, вам необходимо создать новую строку (используя новую строку
, или что у вас есть).
В java-строках - это выявленные объекты и после создания строки он остается в памяти до тех пор, пока она не очищается мусором (и эта уборка не то, что вы можете предпринять как должное).
Когда вы вызываете метод подстроки, Java не создает Trullly New String, но просто сохраняет ряд символов внутри исходной строки.
Итак, когда вы создали новую строку с этим кодом:
this.smallpart = data.substring(12, 18) + "";
Вы фактически создали новую строку при объединении результата с пустой строкой. Вот почему.
Если вы посмотрите на источник подстроки (int, int)
, то увидите, что он возвращает:
new String(offset + beginIndex, endIndex - beginIndex, value);
, где значение
- оригинальное char[]
. Таким образом, вы получаете новую строку, но с тем же самым, лежащим в основе char[]
.
Когда вы делаете это, data.substring() + ""
, вы получаете новую строку с новой, лежащей в основе char[]
.
На самом деле, ваш случай использования - единственная ситуация, в которой вы должны использовать конструктор String(String)
:
String tiny = new String(huge.substring(12,18));
Как документировано в jwz в 1997:
Если у вас есть огромная строка, извлеките из нее подстроку(), держитесь за подстроку и позвольте более длинной строке превратиться в мусор (другими словами, подстрока имеет более длинный срок службы), то байты, лежащие в основе огромной строки, никогда не уйдут.
Я думаю, что это .smallpart хранится Ссылаясь на данные, но почему?
, потому что java строки состоят из массива Char, начните смещение и длину (и кэшированный хэшкод). Некоторые строковые операции, такие как подстрока ()
, создайте новый строковый объект, который разделяет массив Char оригинала, и просто имеет различные поля смещения и / или длины. Это работает, потому что массив CHAR из строки никогда не изменяется после того, как оно было создано.
Это может сохранить память, когда многие подстроки относятся к той же базовой строке без репликации перекрытия деталей. Как вы заметили, в некоторых ситуациях он может сохранить данные, которые больше не нужны от мусора.
«правильный» способ исправления этого является новая строка , конструктор
, то есть
this.smallpart = new String(data.substring(12,18));
, кстати, общий лучший решение, чтобы избежать очень больших строк в первую очередь, и Обработка любого ввода в меньших кусках, AA несколько КБ за раз.
Подводя итог, если вы создаете много подстрок из небольшого количества больших строк, используйте
String subtring = string.substring(5,23)
Поскольку вы используете пространство только для хранения большие строки, но если вы извлекаете лишь горстку маленьких строк из потерянных больших строк, то
String substring = new String(string.substring(5,23));
сократит использование вашей памяти, поскольку большие строки могут быть восстановлены, когда они больше не нужны.
То, что вы вызываете new String
, является полезным напоминанием о том, что вы действительно получаете новую строку, а не ссылку на исходную.