В Java существует две формы создания строковых объектов: одна — литеральная форма, например String str = «droid»;, а другая — использование нового стандартного метода создания объектов, например String str = new String( "droid"); эти два метода часто используются при написании кода, особенно литеральный метод. Однако на самом деле между этими двумя реализациями существуют некоторые различия в производительности и использовании памяти. Все это связано с тем, что для уменьшения повторного создания строковых объектов JVM поддерживает специальную память. Эта память называется пулом строковых констант или пулом строковых литералов.
Принцип работы
Когда в коде создается строковый объект в виде литерала, JVM сначала проверяет литерал. Если в пуле строковых констант есть ссылка на строковый объект с тем же содержимым, ссылка будет возвращена, в противном случае. будет создана новая строка. Объект создается, эта ссылка помещается в пул строковых констант, и ссылка возвращается.
Приведите пример
Буквальная форма создания
Скопируйте код кода следующим образом:
Строка str1 = "дроид";
JVM обнаруживает этот литерал. Здесь мы думаем, что не существует объекта, содержимым которого является droid. JVM не может обнаружить существование строкового объекта с содержимым droid через пул строковых констант, затем она создает строковый объект, затем помещает ссылку на вновь созданный объект в пул строковых констант и возвращает ссылку на переменная стр1.
Если рядом есть такой фрагмент кода
Скопируйте код кода следующим образом:
Строка str2 = "дроид";
Аналогично, JVM все еще необходимо обнаружить этот литерал. JVM выполняет поиск в пуле строковых констант и обнаруживает, что строковый объект с содержимым «droid» существует, поэтому возвращает ссылку на существующий строковый объект в переменную str2. Обратите внимание, что новый строковый объект здесь не создается заново.
Чтобы проверить, указывают ли str1 и str2 на один и тот же объект, мы можем использовать этот код
Скопируйте код кода следующим образом:
System.out.println(str1 == str2);
Результат верный.
Создать, используя новый
Скопируйте код кода следующим образом:
Строка str3 = новая строка («дроид»);
Когда мы используем new для создания строкового объекта, новый строковый объект будет создан независимо от того, существует ли ссылка на объект с тем же содержимым в пуле строковых констант. Поэтому мы используем следующий код для его проверки:
Скопируйте код кода следующим образом:
Строка str3 = новая строка («дроид»);
System.out.println(str1 == str3);
Результат оказался ложным, как мы и думали, что указывает на то, что две переменные указывают на разные объекты.
стажер
Для строкового объекта, созданного с помощью new выше, если вы хотите добавить ссылку на этот объект в пул строковых констант, вы можете использовать метод intern.
После вызова intern сначала проверьте, есть ли ссылка на объект в пуле строковых констант. Если она существует, верните ссылку на переменную. В противном случае добавьте ссылку и верните ее в переменную.
Скопируйте код кода следующим образом:
Строка str4 = str3.intern();
System.out.println(str4 == str1);
Результат вывода верен.
Сложные вопросы
Обязательное условие?
Обязательным условием для реализации пула строковых констант является то, что объект String в Java является неизменяемым, что позволяет безопасно гарантировать, что несколько переменных будут совместно использовать один и тот же объект. Если объект String в Java является изменяемым и ссылочная операция изменяет значение объекта, это также повлияет на другие переменные. Очевидно, это неразумно.
ссылка или объект
Наиболее распространенная проблема заключается в том, хранятся ли ссылки или объекты в пуле строковых констант. В пуле строковых констант хранятся ссылки на объекты, а не объекты. В Java объекты создаются в куче памяти.
Проверка обновления, во многих полученных комментариях тоже обсуждается этот вопрос, я просто проверил. Среда проверки:
Скопируйте код кода следующим образом:
22:18:54-androidyue~/Videos$ cat /etc/os-release
NAME=Федора
ВЕРСИЯ="17 (Мясистое чудо)"
ID=федора
ИДЕНТИФИКАТОР_ВЕРСИИ=17
PRETTY_NAME="Fedora 17 (Мощное чудо)"
ANSI_COLOR="0;34"
CPE_NAME="cpe:/o:fedoraproject:fedora:17"
22:19:04-androidyue~/Videos$ Java-версия
Java-версия "1.7.0_25"
Среда выполнения OpenJDK (fedora-2.3.12.1.fc17-x86_64)
64-разрядная серверная виртуальная машина OpenJDK (сборка 23.7-b01, смешанный режим)
Идея проверки: следующая Java-программа читает видеофайл размером 82 МБ и выполняет внутренние операции в виде строк.
Скопируйте код кода следующим образом:
22:01:17-androidyue~/Videos$ ll -lh | grepWhy_to_learn.mp4
-rw-rw-r--. 1 androidyue androidyue 82M 20 октября 2013 г.Why_to_learn.mp4
Проверочный код
Скопируйте код кода следующим образом:
импортировать java.io.BufferedReader;
импортировать java.io.FileNotFoundException;
импортировать java.io.FileReader;
импортировать java.io.IOException;
общественный класс TestMain {
частная статическая строка fileContent;
public static void main(String[] args) {
fileContent = readFileToString(args[0]);
если (нуль!= fileContent) {
fileContent = fileContent.intern();
System.out.println("Не null");
}
}
частная статическая строка readFileToString (строковый файл) {
Читатель BufferedReader = null;
пытаться {
читатель = новый BufferedReader (новый FileReader (файл));
StringBuffer buff = новый StringBuffer();
Струнная линия;
while ((line = readLine()) != null) {
buff.append(строка);
}
вернуть buff.toString();
} catch (FileNotFoundException e) {
е.printStackTrace();
} catch (IOException e) {
е.printStackTrace();
} окончательно {
if (null != читатель) {
пытаться {
читатель.закрыть();
} catch (IOException e) {
е.printStackTrace();
}
}
}
вернуть ноль;
}
}
Поскольку пул строковых констант существует в постоянной генерации в динамической памяти, он применим до Java8. Мы проверили это, установив для постоянной генерации очень маленькое значение. Если строковый объект существует в пуле строковых констант, то неизбежно возникнет ошибка постоянного пространства java.lang.OutOfMemoryError.
Скопируйте код кода следующим образом:
java -XX:PermSize=6m TestMain ~/Videos/why_to_learn.mp4
Запуск программы проверки не выдал OOM. Фактически, это не может очень точно доказать, сохраняются ли объекты или ссылки.
Но это, по крайней мере, доказывает, что фактический объект содержимого char[] строки не хранится в пуле строковых констант. В этом случае на самом деле не так важно, хранит ли пул строковых констант строковые объекты или ссылки на строковые объекты. Но лично я все же предпочитаю хранить его как справочник.
Преимущества и недостатки
Преимущество пула строковых констант заключается в уменьшении количества создаваемых строк с одинаковым содержимым и экономии места в памяти.
Если мы настаиваем на разговоре о недостатках, то это то, что вычислительное время ЦП приносится в жертву в обмен на пространство. Время вычислений ЦП в основном используется для определения того, существует ли ссылка на объект с таким же содержимым в пуле строковых констант. Однако его внутренняя реализация — HashTable, поэтому стоимость вычислений невелика.
Переработка GC?
Поскольку пул строковых констант содержит ссылки на общие строковые объекты, означает ли это, что эти объекты не могут быть переработаны?
Прежде всего, общие объекты в вопросе, как правило, относительно невелики. Насколько мне известно, эта проблема существовала в более ранних версиях, но с введением слабых ссылок эта проблема должна исчезнуть.
Что касается этой проблемы, вы можете узнать больше об этой статье: Interned Strings: Java Glossary.
стажерское использование?
Предпосылкой для использования intern является то, что вы знаете, что вам действительно нужно его использовать. Например, у нас есть миллионы записей, и определенное значение в записи много раз — Калифорния, США. Мы не хотим создавать миллионы таких строковых объектов. Мы можем использовать intern, чтобы хранить в памяти только одну копию. Может. Для более глубокого понимания intern обратитесь к разделу «Углубленный анализ String#intern».
Всегда ли есть исключения?
Знаете ли вы, что следующий код создаст несколько строковых объектов и сохранит несколько ссылок в пуле строковых констант?
Скопируйте код кода следующим образом:
Проверка строки = "a" + "b" + "c";
Ответ заключается в том, что создается только один объект и в пуле констант сохраняется только одна ссылка. Мы можем это выяснить, используя javap для декомпиляции и просмотра.
Скопируйте код кода следующим образом:
17:02 $ javap -c TestInternedPoolGC
Скомпилировано из «TestInternedPoolGC.java».
публичный класс TestInternedPoolGC расширяет java.lang.Object{
общественный TestInternedPoolGC();
Код:
0: aload_0
1: вызвать специальный #1 //Метод java/lang/Object."<init>":()V;
4: возвращение
public static void main(java.lang.String[]) выдает java.lang.Exception;
Код:
0: ldc #2 //строка abc;
2: асторе_1
3: возвращение
Вы заметили, что во время компиляции эти три литерала были объединены в один? На самом деле это оптимизация, которая позволяет избежать создания избыточных строковых объектов и не вызывает проблем со сращиванием строк. Что касается сращивания строк, вы можете просмотреть подробную информацию о Java: Сращивание строк.