Почему JVM не «видит» повторяющееся значение String в памяти String Pool? - PullRequest
4 голосов
/ 04 августа 2020

Возможно, это дубликат, но я не смог найти нужного мне объяснения. Может кто-нибудь объяснить мне это.

Если я прав:

String s1 = "a";
String s2 = "a";

Оба s1 и s2 будут указывать на один и тот же адрес в пуле строк, и будет только один объект со значением " a ".

Теперь, если я сделаю это:

String s1 = "a"; //line A
s1 = s1.concat("b"); //line B; I don't understand what's happening here in terms of references
String s2 = "ab";//line C
System.out.println(s1 == s2); //false

Почему я получаю false?

Мой взгляд на это (вероятно, неправильный) выглядит следующим образом: вот так:

after line A -> объект создается (со значением a) в пуле строк, на который ссылается s1; after line B -> объект со значением b создается в пуле строк (без ссылки), затем новый объект со значением ab создается в пуле строк (на который ссылается существующий s1) after line C -> (this вероятно, я ошибаюсь) Поскольку существующий объект со значением ab (на который ссылается s1) создается с использованием concat() (или +), JVM не будет повторно использовать этот объект для указания ссылкой s2, он будет вместо этого просто создайте новый объект в пуле строк со значением ab, указанным ссылкой s2;

Где я ошибаюсь?

Ответы [ 4 ]

4 голосов
/ 04 августа 2020

TL; DR - ваше замешательство вызывает модель памяти Java для строк, а именно области Heap и String Constant Pool области память.

Глубокое погружение в модель памяти String

Мотивация дизайна

В Java, String , вероятно, наиболее часто используемый объект. Из-за этого Java поддерживает объекты String со специальной стратегией проектирования памяти, удерживая их либо в Heap , либо в изолированном подмножестве кучи, называемом String Constant Pool , либо в оба.

Пул констант String - это специальное пространство в памяти Heap , в котором хранятся строковые объекты уникальных "literal value" s. Каждый раз, когда вы создаете String с его буквальным значением, JVM сначала проверяет, доступен ли объект с таким же значением в пуле String, и если он есть, возвращается ссылка на тот же объект, если нет - новый объект выделены в пуле констант , и то же самое происходит для всех других созданий литералов String снова и снова.

Причина, почему наличие пула констант - хорошая идея , является семантикой самой фразы, потому что в ней хранятся объекты constant и immutable String, и, как вы видите, это отличная идея для тех случаев, когда вы можете создать много Строковые объекты с одинаковым буквальным содержанием - во всех этих случаях каждый раз будет ссылаться только один объект для одного "literal value", а новые объекты не будут созданы для существующего строкового литерала object.

Обратите внимание, что это возможно только потому, что String неизменяема по определению. Также обратите внимание, что пул строк, который изначально пуст, поддерживается классом String в частном порядке.

Где Java размещает объекты String?

А вот и начинается самое интересное. Важно помнить, что всякий раз, когда вы создаете объект String с помощью инструкции new String(), вы заставляете Java разместить новый объект в Heap ; однако, если вы создаете объект String с "string literal", он выделяется в String Constant Pool . Как мы уже говорили, String Constant Pool существует в основном для уменьшения использования памяти и улучшения повторного использования существующих String объектов в памяти.

Итак, если вы напишете:

String s1 = "a";
String s2 = "a";
String s3 = new String("a");
  1. Строковый объект будет создан в String Constant Pool , а ссылка на этот объект будет сохранена в переменной s1;
  2. Будет выполнен поиск пула строковых констант , а из-за в пуле найден объект с таким же буквальным значением ("a"), будет возвращена ссылка на тот же объект;
  3. String объект будет явно создан в области Heap , и ссылка будет возвращена и сохранена в переменной s3.

Internig Strings

Если вы wi sh перемещаете объект String, созданный с помощью оператора new, в пул констант String , вы можете вызвать метод "your_string_text".intern(); и один из два будет :

  1. , если пул уже содержит ins строку, равную этому объекту String, как определено методом equals (Object), тогда будет возвращена строка из пула;
  2. в противном случае этот объект String будет добавлен в пул и ссылка на него Будет возвращен строковый объект.

Что происходит в вашем коде?

String s1 = "a";
String s2 = "a";

Оба s1 и s2 будут указывать на один и тот же адрес в пуле строк, и будет только один объект со значением «a».

Верно. Первоначально будет создан объект String, который будет помещен в String Constant Pool . После этого, поскольку уже существует String со значением "a", новый объект не будет создан для s2, а ссылка, хранящаяся в s1, будет аналогичным образом сохранена в s2.

Теперь давайте, наконец, посмотрим на ваш вопрос:

String s1 = "a"; //allocated in the String Constant Pool
s1 = s1.concat("b"); //contact() returns a new String object, allocated in the Heap
String s2 = "ab";//"ab" still does NOT exist in the String Constant Pool, and it gets allocated there
System.out.println(s1 == s2); //returns false, because one object is in the Heap, and another is in the String Constant Pool, and as there already exists the object in the pool, with the same value, existing object will be returned by `intern()`.

Если вы, однако, выполните

System.out.println(s1.intern() == s2);

, это вернет true, и я надеюсь, Теперь вы понимаете - почему. Поскольку intern() переместит объект, на который есть ссылка через s1, из Heap в String Constant Pool .

4 голосов
/ 04 августа 2020

From Документация API

publi c String concat (String str) ... Если длина строки аргумента равна 0, возвращается этот объект String. В противном случае создается новый объект String, представляющий последовательность символов, которая представляет собой конкатенацию последовательности символов, представленной этим объектом String, и последовательности символов, представленной строкой аргументов.

2 голосов
/ 04 августа 2020

s1.concat("b") возвращает new String и, следовательно, s1 == s2 будет false.

Это примерно так:

String a = "Hello";
String b = new String("Hello");

System.out.println(a == b); // false

Исходный код String#concat в Java 11 только для справки:

public String concat(String str) {
    int olen = str.length();
    if (olen == 0) {
        return this;
    }
    if (coder() == str.coder()) {
        byte[] val = this.value;
        byte[] oval = str.value;
        int len = val.length + oval.length;
        byte[] buf = Arrays.copyOf(val, len);
        System.arraycopy(oval, 0, buf, val.length, oval.length);
        return new String(buf, coder);
    }
    int len = length();
    byte[] buf = StringUTF16.newBytesFor(len + olen);
    getBytes(buf, 0, UTF16);
    str.getBytes(buf, len, UTF16);
    return new String(buf, UTF16);
}
1 голос
/ 04 августа 2020

Я полагаю, что метод String concat() создает экземпляр new String("ab"), который отличается от объекта "ab".

...