String — важный тестовый сайт для основы Java. Есть много вопросов, которые нужно задать, и многие вопросы можно сократить по горизонтали на другие тестовые площадки или по вертикали вглубь JVM.
В этой статье не рассматриваются основы String и основное внимание уделяется String#intern().
Пул строковых констант
Строковые константы могут попадать в пул констант два раза:
- Время компиляции: объявить в двойных кавычкахконстанты (включаяпоказать заявление,Оптимизация статической компиляцииПоследние константы, такие как «1» + «2», оптимизированы до константы «12»), которая будет статически записана в «пул констант» в файле класса во время внешней компиляции. «Постоянный пул» будет загружен в «постоянный пул в памяти» после загрузки класса, который мы обычно называем постоянным пулом. в то же время,JIT-оптимизацияПодобные константы также могут быть сгенерированы.
- Время выполнения: вызов метода String#intern(), можно динамически записать объект String в указанный выше «пул констант в памяти».
Поведение тайминга 1 понятно. Принцип может читать структуру файла класса, загрузку класса, оптимизацию времени компиляции или выполнения и так далее.
Время 2 ведет себя по-разному в jdk6 и jdk7, обсуждаемых ниже.
String#intern()
Читатели могут напрямую читать ссылки. Следующее резюме предназначено только для удобства обзора обезьян.
утверждение
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class <code>String</code>.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this <code>String</code> object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this <code>String</code> object is added to the
* pool and a reference to this <code>String</code> object is returned.
* <p>
* It follows that for any two strings <code>s</code> and <code>t</code>,
* <code>s.intern() == t.intern()</code> is <code>true</code>
* if and only if <code>s.equals(t)</code> is <code>true</code>.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
String#intern() — это нативный метод. Согласно Javadoc, если текущая строка существует в пуле констант, текущая строка будет возвращена напрямую.Если строка не существует в пуле констант, строка будет помещена в пул констант, а затем возвращена.
Принцип реализации
Наконец, JNI вызывает метод StringTable::intern(), реализованный в C++:
oop StringTable::intern(Handle string_or_null, jchar* name,
int len, TRAPS) {
unsigned int hashValue = java_lang_String::hash_string(name, len);
int index = the_table()->hash_to_index(hashValue);
oop string = the_table()->lookup(index, name, len, hashValue);
// Found
if (string != NULL) return string;
// Otherwise, add to symbol to table
return the_table()->basic_add(index, string_or_null, name, len,
hashValue, CHECK_NULL);
}
oop StringTable::lookup(int index, jchar* name,
int len, unsigned int hash) {
for (HashtableEntry<oop>* l = bucket(index); l != NULL; l = l->next()) {
if (l->hash() == hash) {
if (java_lang_String::equals(l->literal(), name, len)) {
return l->literal();
}
}
}
return NULL;
}
Найдите строку в хеш-таблице, возвращаемую функцией the_table(), верните ее, если она существует, иначе присоединитесь к таблице.
StringTable — этоисправленный размеризHashtable, размер по умолчанию — 1009. Основная логика такая же, как у HashMap в Java, и для решения проблемы коллизий также используется метод zipper.
Поскольку это метод застежки-молнии, если вставить много строк, коллизия будет усугубляться, что приведет к очень длинному связанному списку. В худшем случае производительность String#intern() снижается с O(1) до O(n).
- Длина StringTable в jdk6 фиксирована и равна 1009.
- В jdk7 длину StringTable можно передать как параметр
-XX:StringTableSize
Укажите, по умолчанию 1009.
Разница между String#intern() в jdk6 и jdk7
введение
Я полагаю, что многие Java-программисты делают то же самое.String s = new String("abc");
Этот оператор создает заголовок нескольких объектов. В этом разделе в основном исследуется мастерство программиста в работе с константным пулом строковых объектов. Приведенный выше оператор создает 2 объекта:
- Первый объект с содержимым «abc» хранится в пуле констант.
- Второй объект, содержимое «abc», хранится в куче.
проблема
Взгляните на кусок кода:
public static void main(String[] args) {
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
}
распечатать результат:
# jdk6下
false false
# jdk7下
false true
Конкретная причина будет объяснена позже, а затемs3.intern();
Заявление опускается на одну строку и помещается вString s4 = "11";
позже. будетs.intern();
ставитьString s2 = "1";
позже:
public static void main(String[] args) {
String s = new String("1");
String s2 = "1";
s.intern();
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
System.out.println(s3 == s4);
}
распечатать результат:
# jdk6下
false false
# jdk7下
false false
Объяснение jdk6
Примечание. Зеленая линия на рисунке представляет собой указатель содержимого объекта String, а черная линия — указатель адреса.
В jdk6 все приведенные выше отпечатки ложны.
Потому что постоянный пул jdk6 размещен в Пермской области, которая полностью отделена от обычной Кучи (имеются в виду области Эдем, Выживший и Старый). А именно: Строки, объявленные в кавычках, напрямую загружаются в пул констант посредством компиляции и загрузки классов и располагаются в области Perm; новые объекты String располагаются в Heap (E, S, O). Сравнение адреса объекта Пермского края с адресом объекта в Куче — это точно не одно и то же.
Пермская область в основном хранит некоторую загруженную информацию о классах, статические переменные, фрагменты методов, константные пулы и т.д.
Объяснение jdk7
В jdk6 и более ранних версиях пул строковых констант размещается в Пермской области. По умолчанию размер Пермской области всего 4М.Если поставить более крупные стринги, то легко скинутьOutOfMemoryError: PermGen space
.
Поэтому jdk7 переместил пул строковых констант из пермской области в обычную кучу (E, S, O).
Пермский край – это постоянное поколение. Легко столкнуться с переполнением памяти, когда область методов реализована самой постоянной генерацией, а содержимое, хранящееся в области методов, также трудно оценить по размеру, и нет необходимости управлять им в куче. jdk8 отменил постоянную генерацию и создал новую область метода реализации Metaspace вне кучи.
Именно потому, что пул строковых констант был перемещен в кучу, произошли вышеуказанные изменения.
первый кусок кода
Сначала посмотрите на s3 и s4:
- Во-первых,
String s3 = new String("1") + new String("1");
, создание нескольких объектов,s3 в конечном итоге указывает на «11» в куче. Обратите внимание, что в настоящее время в пуле констант нет строки «11». - Потом,
s3.intern();
, поместите строку «11» в s3 в пул констант, поскольку в настоящее время строка «11» не существует в пуле констант, поэтому общая практика такая же, как у jdk6, и создается объект String «11». в пуле констант ——Однако пул констант в jdk7 уже не в пермской области, и внесены соответствующие коррективы: не нужно хранить объект в пуле констант, а напрямую хранить ссылку в куче, то есть ссылочный адрес s3. - Следующий,
String s4 = "11";
, "11" объявляется в двойных кавычках, поэтому для его поиска он будет напрямую обращаться к пулу констант, если он не будет создан снова. Обнаружено, что эта строка уже существует, которая только что была переданаs3.intern();
Ссылочный адрес s3 хранится в пуле констант. Итак, напрямую верните ссылочный адрес s3,s4 назначается ссылкой на s3, а s4 указывает на «11» в куче. - Наконец, «11» в куче, на которую указывают s3 и s4, ссылка на s3 хранится в пуле констант, что удовлетворяет
s3 == s4
.
Посмотрите на s и s2 еще раз:
- Во-первых,
String s = new String("1");
, генерируются 2 объекта, "1" в пуле констант и "1" в куче,s указывает на "1" в куче. - Потом,
s.intern();
, предыдущее предложение уже создало "1" в пуле констант, так что здесь ничего не делайте. - Следующий,
String s2 = "1";
, в постоянном пуле есть "1", поэтомуs2 указывает непосредственно на «1» в пуле констант. - Наконец, «1» в куче, на которую указывает s, «1» в пуле констант, на который указывает s2, и строка «1», хранящаяся в пуле констант, не удовлетворяется.
s == s2
.
второй кусок кода
Сначала посмотрите на s3 и s4,s3.intern();
ставитьString s4 = "11";
Задний:
- выполнить первым
String s4 = "11";
, в настоящее время «11» не существует в пуле констант, поэтому поместите «11» в пул констант, а затемs4 указывает на «11» в постоянном пуле. - повторно выполнить
s3.intern();
, предыдущее предложение уже создало "11" в пуле констант, так что здесь ничего не делайте. - Наконец, s3 по-прежнему указывает на «11» в куче, s4 указывает на «11» в пуле констант, а строка «11» хранится в пуле констант, что больше не выполняется.
s3 == s4
.
Глядя на s и s2 снова,s.intern();
ставитьString s2 = "1";
Задний:
- выполнить первым
String s2 = "1";
, ранее пройденныйString s = new String("1");
"1" создается в постоянном пуле, поэтомуs2 указывает непосредственно на «1» в пуле констант. - повторно выполнить
s.intern();
, в константном пуле есть "1", поэтому здесь ничего не делается. - Наконец, «1» в куче, на которую указывает s, «1» в пуле констант, на который указывает s2, и строка «1», хранящаяся в пуле констант, по-прежнему не выполняется.
s == s2
.
Сводка различий
По сравнению с jdk6, jdk7 изменил расположение пула констант String и семантику String#intern():
- Перемещен пул констант String из области Perm в область Heap.
- При вызове метода String#intern(), если строка существует в куче, но не в пуле констант, ссылка на объект в куче напрямую сохраняется в пуле констант без повторного создания объекта в пуле констант.
использовать позу
Рекомендуется прямое чтение ссылок.
дополнительный вопрос
Основное использование String#intern() выглядит следующим образом:
String s1 = xxx1.toString().intern();
String s2 = xxx2.toString().intern();
assert s1 == s2;
Тем не мение,xxx1.toString()
,xxx2.toString()
Были созданы два анонимных объекта String, после чего вызывается String#intern(). Так,Куда делись эти два анонимных объекта??
Подсчитано, что у обезьяны проблемы с пониманием процесса создания объектов, возможно
xxx1.toString()
Возврат без сохранения объекта в кучу? Может быть, какой-то синтаксический сахар в String#intern()?Позже будет время ее решить. . .
Ссылаться на:
Ссылка на эту статью:
Ссылка на эту статью:Пул строковых констант и String#intern()
автор:обезьяна 007
Источник:monkeysayhi.github.io
Эта статья основана наCreative Commons Attribution-ShareAlike 4.0Международное лицензионное соглашение выпущено, его можно перепечатать, вывести или использовать в коммерческих целях, но авторство и ссылка на эту статью должны быть сохранены.