Будет ли.hashcode() возвращать другой int из-за сжатия пространства владения?
Если я позвоню Object.hashcode()
Метод для некоторого объекта возвращает внутренний адрес объекта (реализация по умолчанию). Этот адрес является логическим или физическим адресом?
В сборке мусора из-за сжатия объектов памяти происходит смещение в памяти. Если я вызову хэш-код до и после GC, он вернет один и тот же хэш-код (он возвращает), и если да, то почему (из-за того, что адрес сжатия может измениться)?
5 ответов
@erickson более или менее правильно. Хэш-код, возвращаемый java.lang.Object.hashCode()
не изменяется за время существования объекта.
Способ, которым это (обычно) реализовано, довольно умный. Когда объект перемещается сборщиком мусора, его оригинальный хеш-код должен храниться где-то на случай, если он будет использован снова. Очевидный способ реализовать это - добавить 32-битное поле в заголовок объекта для хранения хеш-кода. Но это добавило бы 1 слово к каждому объекту и потратило бы место в наиболее распространенном случае... где объект hashCode
метод не вызывается.
Решение состоит в том, чтобы добавить два бита флага к слову флага объекта и использовать их (примерно) следующим образом. Первый флаг устанавливается, когда hashCode
метод называется. Второй флаг говорит hashCode
метод, использовать ли текущий адрес объекта в качестве хеш-кода или использовать сохраненное значение. Когда GC запускается и перемещает объект, он проверяет эти флаги. Если первый флаг установлен, а второй не установлен, ГХ выделяет одно дополнительное слово в конце объекта и сохраняет исходное местоположение объекта в этом слове. Затем он устанавливает два флага. С тех пор, hashCode
Метод получает значение хеш-кода из слова в конце объекта.
На самом деле, identityHashCode
Реализация должна вести себя таким образом, чтобы удовлетворить следующую часть общего контракта hashCode:
"Всякий раз, когда он вызывается для одного и того же объекта более одного раза во время выполнения приложения Java, метод hashCode должен последовательно возвращать одно и то же целое число, при условии, что никакая информация, используемая в сравнениях сравнения объекта, не изменяется. Это целое число не должно оставаться согласованным с одно исполнение приложения к другому исполнению того же приложения. "
Гипотетическая реализация identityHashCode()
то, что просто вернуло текущий машинный адрес объекта, нарушило бы выделенную часть, если / когда ГХ переместил объект по другому адресу. Единственный способ обойти это (для гипотетической) JVM - гарантировать, что объект никогда не перемещается один раз. hashCode
был призван на это. И это привело бы к серьезным и неразрешимым проблемам с фрагментацией кучи.
Нет, хеш-код объекта по умолчанию не изменится.
Документация не говорит, что хеш-код является адресом, она говорит, что она основана на адресе. Учтите, что хэш-коды 32-битные, но есть 64-битные JVM. Ясно, что непосредственное использование адреса не всегда будет работать.
Реализация зависит от JVM, но в JVM от Sun (Oracle) я считаю, что хэш-код кэшируется при первом обращении к нему.
По договору hashCode он не может измениться по такой причине.
В этой ссылке говорится, что действительно хеш-код по умолчанию - это адрес JVM объекта, но если он перемещается - адрес остается согласованным. Я не знаю, насколько надежен этот источник, но я уверен, что разработчики этого метода продумали этот сценарий (что не является редким или угловым случаем) и обеспечили правильную функциональность этого метода.
Если хеш-код изменится, объект исчезнет в хэш-наборе, в который он был вставлен, и Sun зальет жалобами.