java, java and. java
Наверняка вам известно, что при сравнении двух объектов в Java на == обычно получается не тот результат, что нужен, т.к. идет сравнение ссылок на объекты, а не данных этих объектов.
Хм, давайте тогда посмотрим на следующий код и на результат его выполнения:
public class Temp < public static void main(String []args)< Integer i1 = 10; Integer i2 = 10; System.out.println(i1 == i2); >>
Как думаете, что он напечатает? А напечатает он true.
А вот еще один код:
public class Temp < public static void main(String []args)< Integer i1 = 130; Integer i2 = 130; System.out.println(i1 == i2); >>
Этот код уже выведет false.
На первый взгляд странно, не так ли? Сейчас объясню.
В Java есть пул(pool) целых чисел в промежутке [-128;127]. Т.е. если мы создаем Integer в этом промежутке, то вместо того, чтобы каждый раз создавать новый объект, JVM берет их из пула. Таким образом, в первом примере i1 и i2 указывают на один и тот же объект из пула, а во втором — создаются два разных объекта. Отсюда и результат.
Стоит заметить, что в приведенных выше примерах не использовалось ключевое слово new.
Пример:
public class Temp < public static void main(String []args)< Integer i1 = new Integer(10); Integer i2 = new Integer(10); System.out.println(i1 == i2); >>
Результатом будет false. Тут мы уже явно говорим, что хотим создать новый(new) объект, так что ссылки разные.
Вот и всё.
Пул обёрток над примитивами
Значения входящие в диапазон [-128; 127], у оберток на примитивами хранятся не напрямую в хипе, а в неком пуле для более быстрого доступа к ним. Как по отношению к хипу располагается этот пул(является ли пул частью хипа)? + На сколько я понимаю похожая ситуация и с пулов String-ов
Отслеживать
задан 3 сен 2018 в 16:36
Андрей Козицкий Андрей Козицкий
1,038 1 1 золотой знак 7 7 серебряных знаков 23 23 бронзовых знака
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Судя по коду, кеш-интов, это обычный Integer массив, который заполняется статическим блоком :
private static class IntegerCache < static final int low = -128; static final int high; static final Integer cache[]; static < // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) < try < int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); >catch( NumberFormatException nfe) < // If the property cannot be parsed into an int, ignore it. >> high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; >
Поэтому он хранится в хипе.
Отслеживать
ответ дан 3 сен 2018 в 16:56
aleshka-batman aleshka-batman
2,868 10 10 серебряных знаков 21 21 бронзовый знак
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.10.27.43697
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
String Pool в Java
Java даёт выбор между примитивными типами данных и объектными. Одни передаются по значению, другие по ссылке. Одни занимают предсказуемое количество памяти, другие не очень (конечно только если Вы не знаете размеры метаинформации класса, для которого хотите произвести расчёт). Под одних память выделяется на стеке, под другие в heap’е. Они сильно отличаются друг от друга
Элементы в пределах своего типа (примитивный или объектный) ведут себя похоже, независимо от конкретного типа данных. Значения int ведут себя так же, как и значения типа short. В объектных типах данных схожая ситуация. Но есть исключения. Например — объектный тип String.
Что такое String?
String — это класс в Java, то есть объектный тип. Он описывает строки и хранит их данные в массиве char.
Оговорка:
Тип char используется в старых версиях Java, например 8-ой. В Java 11 используется уже массив byte’ов.
Сколько памяти занимает String? Примитивный тип char в Java имеет размер 2 byte’а. То есть один символ занимает в памяти 2 байта. Теперь представьте — каждый раз когда мы используем строку в Java, будь-то имя пользователя или ссылку на какой-либо сайт, мы создаём в системе большой массив char. Это занимает память. В объектных типах помимо всех ссылок на объекты и примитивов, память занимает ещё и заголовочная информация класса.
Зачем это знать? Строки — самый популярный тип данных в Java. Огромное количество данных описывается строками. Ещё более интересн тот факт, что строки в одних и тех же программах часто повторяются.
String — это immutable тип. То есть, после создания объекта этого типа поменять его значение нельзя. Отсюда делаем вывод — смысла в создании новых объектов типа String со значениями, для которых объекты уже были созданы — нет. Это не целесообразно, потому что каждый раз выделять память под одно и тоже, что ещё и не возможно изменить — чревато чрезмерным потреблением памяти.
Создатели Java заранее позаботились об этой проблеме и сделали тип String не совсем обычным объектным типом.
Что такое строковый пул?
Строковый пул или String pool — это особое место в heap’е, куда попадают объекты типа String после их создания. Он выполняет функцию кеша строк. Каждый раз, когда Вы создаёте строку, она попадает в строковый пул. Если же на момент создания новой строки пул уже содержит такое же значение, то вместо создания нового объекта возвращается тот, что уже лежит в пуле.
У Вас есть возможность влиять на это поведение и не класть объекты в пул, если требуется.
Как работать со строковым пулом?
Разберёмся с тем как работает String pool на практике.
Обычно строки в Java программах объявляются так:
String text = “Hello World”;
Что происходит в JVM за кадром? Остановитесь и подумайте. Если Вашим ответом будет что-то вроде — «В heap’е будет выделена память под строковый объект и ссылка на него будет возвращена и присвоена локальной переменной text» — Вы правы.
String text = “Hello World”;
String text2 = “Hello World”;
А что произойдёт тут? Если Вы думаете, что на обе локальные переменные будет выделена память в heap’е — Вы ошибаетесь. Именно в этом примере и видно результат работы пула строк.
В Java все строки, объявленные в виде литералов, то есть так:
“My String”
автоматически попадают в строковый пул. Любая попытка объявить новую переменную задав ей значение с помощью литерала приводит к тому, что JVM проверяет наличие такой строки в пуле строк и возвращает объект из него, если объект с таким значением обнаружен.
Как убедиться в том, действительно ли обе переменные указывают на один и тот же объект? Очень просто. Достаточно посмотреть что будет выведено в результате следующей операции:
System.out.println(text == text2);
Не спешите набирать код, я Вам подскажу — ответом будет `true`. Это означает — обе переменные указывают на один и тот же объект.
А как тогда сделать так, чтобы строки в пул не попадали? Это тоже сделать просто. Используйте конструктор явно при создании строк. Вот так:
String text = new String(“text”);
В этом случае, объект типа String будет создан, память под него будет выделена в heap’е, но в строковый пул он не попадёт. Это легко проверяется следующим примером:
String text = “Hello World”;
String text2 = new String(“Hello World”);
System.out.println(text == text2);
Результатом работы кода выше будет `false`, потому что теперь ссылки указывают на два разных объекта.
Ну и наконец, как добавить строку в строковый пул после её создания? Для этого класс String содержит метод под названием `intern()`. Именно он отвечает за сохранение текущего объекта String в пул строк. Пример использования:
String text = “Hello World”;
String text2 = new String(“Hello World”);
System.out.println(text == text2); // выведет falsetext2 = text2.intern(); // попытается положить текущую строку в пул строк, обнаружит что такое значение уже там есть и вернёт объект из строкового пула. То есть тот, на который указывает переменная text
System.out.println(text == text2); // выведет true
Зачем знать о строковом пуле?
Строковый пул несёт не только пользу. Если не знать о его существовании и принципах работы, можно легко получить дыру в безопасности приложения. Сделать это довольно просто — достаточно добавить в пул какой-нибудь пароль или логин. Содержимое строкового пула доступно в memory dump’ах, к которым Вы или кто-то другой может получить доступ.
Надеюсь эта статья поможет Вам писать код более осознанно, ведь, теперь Вы знаете, что за строками в Java стоит String pool.
Кеширование или Integer пул?
Как работает кеширование при операции autoboxing? Начиная с Java 1.5, для целочисленных значений в диапазоне от -128 до + 127 объекты-обёртки кешируются внутри для повторного использования. В классе-обёртке Integer есть внутренний класс IntegerCache. Он объявлен как private static. В этом внутреннем классе кешированные объекты находятся в массиве cache[]. Кеширование выполняется при первом использовании класса-обёртки. После первого использования, вместо создания нового экземпляра (кроме использования конструктора), используются кешированные объекты. Код метода valueOf() класса Integer выгдядит так:
Кэширование касается не только класса-оболочки Integer. Имеются аналогичные реализации кеширования для других классов-оболочек целочисленных типов: ByteCache, ShortCache, LongCache, CharacterCache. Кешированные объекты не используются при создании объекта-обёртки с помощью конструктора. На хабре есть статья, в которой SSiarhei (предполагаю, что крутой программист, потому что сдавал экзамен Oracle Certified Professional Java Programmer), в 2011 году написал: https://habr.com/ru/post/111189/
Так что происходит на самом деле? Есть ли Integer пул, и если да, то какую роль он играет?