Будет ли .hashcode() возвращать другой int из-за уплотнения пространства владения?

Если я вызываю метод Object.hashcode() для некоторого объекта, он возвращает внутренний адрес объекта (реализация по умолчанию). Является ли этот адрес логическим или физическим адресом?

В сборке мусора из-за смещения памяти происходит перемещение объектов в памяти. Если я вызову hashcode до и после GC, он вернет тот же хэш-код (он вернется), и если да, то почему (из-за изменения адреса уплотнения)?

Ответ 1

@erickson более-менее корректен. Хэш-код, возвращаемый java.lang.Object.hashCode(), не изменяется для времени жизни объекта.

То, как это (обычно) реализовано, довольно умно. Когда объект перемещается сборщиком мусора, его исходный хэш-код должен храниться где-то в случае его повторного использования. Очевидным способом реализации этого было бы добавить 32-битное поле в заголовок объекта для хранения хэш-кода. Но это добавит накладные расходы на 1 слово для каждого объекта и потеряет пространство в наиболее распространенном случае... где не будет вызван метод Object hashCode.

Решение состоит в том, чтобы добавить два слова флага в слово флага объекта и использовать их (грубо) следующим образом. Первый флаг устанавливается при вызове метода hashCode. Второй флаг сообщает hashCode, использовать ли текущий адрес объекта в качестве хэш-кода или использовать сохраненное значение. Когда GC запускается и перемещает объект, он проверяет эти флаги. Если первый флаг установлен, а второй отключен, GC выделяет одно дополнительное слово в конце объекта и сохраняет исходное местоположение объекта в этом слове. Затем он устанавливает два флага. С этого момента метод hashCode получает значение hashcode из слова в конце объекта.


Фактически реализация identityHashCode должна вести себя таким образом, чтобы удовлетворить следующую часть общего контракта hashCode:

"Всякий раз, когда он вызывается на одном и том же объекте более одного раза во время выполнения приложения Java, метод hashCode должен последовательно возвращать одно и то же целое число, если информация, используемая при равных сравнениях с объектом, не изменяется. Это целое число не должно оставаться согласованным с одним исполнением приложения на другое выполнение того же приложения."

Гипотетическая реализация identityHashCode(), которая просто вернула машинный адрес current объекта, нарушит выделенную часть, если/когда GC переместил объект на другой адрес. Единственный путь вокруг этого - для (гипотетического) JVM гарантировать, что объект никогда не перемещается, когда на него был вызван hashCode. И это приведет к серьезным и неразрешимым проблемам с фрагментацией кучи.

Ответ 2

Нет, хэш-код по умолчанию для объекта не изменится.

В документации не указано, что хеш-код является адресом, он говорит, что он основан на адресе. Рассмотрим, что хэш-коды 32 бита, но есть 64-битные JVM. Очевидно, что прямое использование адреса не всегда будет работать.

Реализация зависит от JVM, но в Sun (Oracle) JVM я считаю, что хэш-код кэшируется при первом доступе.

Ответ 3

В этой ссылке говорится, что действительно хэш-код по умолчанию - это JVM-адрес объекта, но если он перемещен - адрес остается последовательны. Я не знаю, насколько достоверен этот источник, но я уверен, что разработчики этого метода подумали об этом сценарии (что не является редким или угловым) и обеспечили правильную функциональность этого метода.

Ответ 4

По контракту hashCode он не может измениться по этой причине.

Ответ 5

если хэш-код изменится, объект исчезнет в наборе хэшей, в который он был вставлен, и Sun будет залита жалобами.