Пул объектов для Java-оболочек и строк
Как вы все очень хорошо знаете, иногда Java использует пулы объектов для оболочек и строковых типов, иногда это не так.
например:
Integer i1 = 1;
Integer i2 = 1;
Integer i3 = new Integer(1);
String s1 = "String";
String s2 = "String";
String s3 = new String ("String");
System.out.println("(i1 == i2) " + (i1 == i2));
System.out.println("(i2 == i3) " + (i2 == i3));
System.out.println("(s1 == s2) " + (s1 == s2));
System.out.println("(s2 == s3) " + (s2 == s3));
Execution result:
(i1 == i2) true
(i2 == i3) false
(s1 == s2) true
(s2 == s3) false
Как вы видите, бокс примитивов берет объекты из пула, создание строк через строковый литерал также берет объекты из пула. Такие объекты фактически являются одним и тем же объектом (оператор == возвращает true на них).
другие механизмы создания оболочек и строк не принимают предметы из бассейна. Объекты, созданные этими способами, на самом деле являются разными объектами (оператор == возвращает false на них).
меня смущает тот факт, что пул используется частично.
Если это проблема с памятью, почему бы не использовать пул все время? Если это не проблема памяти-зачем использовать его вообще?
вопрос в том-каковы причины реализации такого поведения (=частичное использование пула)?
вопрос скорее теоретические, но и практическое применение - он может помочь понять, как правильно использовать пользовательские объекты, бассейны и, конечно, понимание того, как Java работает всегда хорошо.
5 ответов
его скорость озабоченность, выделяя новый Integer
каждый раз будет стоить как времени, так и памяти. Но по той же причине выделение при запуске слишком много при запуске использует тонны памяти и времени.
результатом является этот странный компромисс, который у нас есть. Причины такого поведения обсуждаются в стандарте Java. (5.7)
если заданное значение p равно true, false, байт, символ в диапазоне от \ u0000 до \u007f или int или короткое число между -128 и 127, тогда пусть r1 и r2-результаты любых двух боксерских преобразований p. Это всегда так, что r1 = = r2. В идеале бокс заданного примитивного значения p всегда будет давать идентичную ссылку. На практике это может оказаться невозможным с использованием существующих методов осуществления. Приведенные выше правила представляют собой прагматический компромисс. Последнее предложение выше требует, чтобы определенные общие значения всегда были в коробке в неразличимые предметы. Реализация может кэшировать их, лениво или нетерпеливо.
для других значений эта формулировка запрещает любые предположения о тождестве упакованных значений со стороны программиста. Это позволит (но не потребует) совместного использования некоторых или всех этих ссылок.
Это гарантирует, что в большинстве распространенных случаев поведение будет желательным, без наложения чрезмерного штрафа за производительность, особенно на небольших устройствах. Меньше реализации с ограниченной памятью могут, например, кэшировать все символы и шорты, а также целые числа и длины в диапазоне-32K - +32K.
tl; dr
невозможно заставить его работать идеально, и это слишком странно, чтобы не работать вообще. Таким образом, у нас это работает "большую часть" времени.
Если это проблема с памятью, почему бы не использовать пул все время?
объединение всего все время дороже, чем простой кэш, который иногда терпит неудачу: вам нужны более сложные структуры данных (объединение небольших целых чисел можно сделать с помощью простого и небольшого массива) и алгоритмы, и вы всегда должны идти, хотя эта проверка, даже когда пул не поможет вам. Кроме того, вы объедините много объектов, которые никогда не понадобятся снова, что является пустой тратой памяти: Вам нужно больше (бесполезных) записей кэша, и вам нужно управлять этим кэшем (или страдать от сохранения бесполезных объектов).
Если это не проблема с памятью-зачем использовать его вообще?
Это is проблемы с памятью. Такая оптимизация экономит много памяти. Объединение объект не обязательно уменьшает использование памяти, потому что не каждый объект используется много. Это обмен. Подход, который был принят, экономит значительную сумму памяти для некоторых распространенных случаев использования, без замедления других операций чрезмерно или тратить много памяти.
существуют различные соображения. Например, использование памяти, но и семантика языка.
new Integer(1);
является явным созданием объекта. Замена этого оператора оператором "get from pool" изменит семантику языка.
Integer.valueOf(1)
является явным "взять из пула, если в диапазоне пула". Обратите внимание, что пул статичен и реализован на Java, а не на виртуальной машине. Вы можете посмотреть его:java.lang.Integer$IntegerCache
. Я полагаю, что спецификации Java говорят, что приведение от int
до Integer
сделаны вставки Integer.valueOf
звонок.
теперь, если вы посмотрите, как этот кэш реализован, вы заметите, что есть настраиваемый пользователем параметр для размера кэша. По умолчанию этот кэш состоит только из массива Integer[256]
сохранение предварительно инициализированных экземпляров для -128..127
(т. е. диапазон значений байта). По-видимому, этот диапазон значений предлагает лучший компромисс производительности / памяти для общего использования.
см., например, http://martykopka.blogspot.de/2010/07/all-about-java-integer-cache.html для получения более подробной информации.
если вы проводите некоторое время, работая над численными вычислениями на Java, вы do почувствуйте негативное влияние автобоксинга. Для чисел высокой эффективности, первое эмпирическое правило во избежание любой вид autoboxing. Например, GNU Trove предлагает хэш-карты и аналогичные структуры для примитивных типов, а преимущества среды выполнения и памяти необъятный.
An Integer
объект занимает 16 байт памяти-в 4 раза больше, чем int
. Так, например, выше кэш занимает около 5 кб памяти. Это то, что большинство приложений может позволить себе тратить впустую. Но, очевидно, вы не можете сделать это для всех чисел!
что касается строк, компилятор должен хранить их соответствующим образом в файле класса. Итак, как вы храните строку в файле класса? Самый простой способ-переписать код что-то вроде это:
private static final char[] chars_12345 = new char[]{ 't', 'e', 's', 't'};
private static final String CONST_STRING_12345 = new String(chars_12345);
который не полагается на любую магическую обработку String
тип. Это просто завернутый набор примитивов. И, конечно, вы хотите хранить каждую уникальную строку только один раз в классе, чтобы уменьшить размер класса и, следовательно, время загрузки.
Это случай памяти и скорости. Вы действительно не хотите создавать 4 миллиарда целочисленных объектов при запуске JVM, потому что большую часть времени они никогда не будут использоваться.
для строк также существует проблема поиска соответствующих строк в интернированном пуле.
строковые литералы в исходном коде просты, поскольку компилятор может найти и организовать их интернирование, но если я создаю строку динамически во время выполнения, JVM придется пройти через пул и искать каждый строка, чтобы увидеть, соответствует ли она и может быть повторно использована, а строки могут
пока есть структуры данных, которые могут помочь ускорить, это вообще просто проще и быстрее создать новый объект.
см., следующий сегмент кода:
Integer i1=1;
Integer i2=2;
String s1="String";
String s2="String";
на i1
, i2
являются ссылками только не объекты, им стенд присваивается объект 1
.
Точно так же!--5-->, s2
являются ссылками только не объекты, им стенд присваивается объект "String"
.
, тогда как в следующем коде:
Integer i3=new Integer(1);
String s3=new String("String");
на new
оператор создает new
Object
.
Надеюсь, я тебя оправдал.