Это пятый день, когда я участвую в Gengwen Challenge. Ознакомьтесь с подробностями мероприятия:Обновить вызов
Ставь лайк и потом смотри, вырабатывай полезную привычку
Сначала рассмотрим простую печать.
System.out.println(new Object());
выведет полное имя класса класса и строку:
java.lang.Object@6659c656
@
Что после символа? Это хэш-код или адрес памяти объекта? Или какое-то другое значение?
фактически@
Ниже приведено значение хэш-кода объекта, хэш-код, отображаемый в шестнадцатеричном формате, для проверки:
Object o = new Object();
int hashcode = o.hashCode();
// toString
System.out.println(o);
// hashcode 十六进制
System.out.println(Integer.toHexString(hashcode));
// hashcode
System.out.println(hashcode);
// 这个方法,也是获取对象的 hashcode;不过和 Object.hashcode 不同的是,该方法会无视重写的hashcode
System.out.println(System.identityHashCode(o));
Выходной результат:
java.lang.Object@6659c656
6659c656
1717159510
1717159510
Как генерируется хэш-код объекта? Это действительно адрес памяти?
Содержание этой статьи основано на JAVA 8 HotSpot.
Логика генерации hashCode
Логика генерации hashCode в JVM не так проста, она предоставляет несколько стратегий, каждая из которых дает разные результаты.
Давайте посмотрим на hashCode, сгенерированный в исходном коде openjdk.основной метод:
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = intptr_t(obj) ;
} else {
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}
Из исходного кода можно узнать, что стратегия генерации состоит изhashCode
управляется глобальной переменной, значение по умолчанию равно 5, и эта переменная определена в другомголовной файлвнутри:
product(intx, hashCode, 5,
"(Unstable) select hashCode generation algorithm" )
Исходный код очень ясен...(нестабильно) выберите алгоритм, сгенерированный hashCode, и определение здесь может управляться параметрами запуска jvm, сначала подтвердите значение по умолчанию:
java -XX:+PrintFlagsFinal -version | grep hashCode
intx hashCode = 5 {product}
openjdk version "1.8.0_282"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_282-b08)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.282-b08, mixed mode)
Таким образом, мы можем настроить различные алгоритмы генерации хэш-кода через параметры запуска jvm и протестировать результаты генерации по разным алгоритмам:
-XX:hashCode=N
Теперь давайте посмотрим на различную производительность каждого алгоритма генерации хэш-кода.
Алгоритм 0
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random();
}
Этот алгоритм генерации с использованиемPark-Miller RNGСтратегия генерации случайных чисел. Однако следует отметить, что... этот случайный алгоритм будет иметь ожидание вращения при высоком уровне параллелизма.
Алгоритм 1
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
}
Этот алгоритм действительно является адресом памяти объекта и напрямую получает адрес памяти объекта.intptr_t
указатель типа
Алгоритм 2
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
}
Это не нужно объяснять... Для внутренних тестовых сценариев следует использовать фиксированный возврат 1.
Заинтересованные студенты могут попробовать-XX:hashCode=2
Давайте запустим этот алгоритм и посмотрим, все ли результаты hashCode равны 1.
Алгоритм 3
if (hashCode == 3) {
value = ++GVars.hcSequence ;
}
Этот алгоритм тоже очень простой, он самоинкрементный, хэш-код всех объектов использует эту самоинкрементную переменную. Попробуем эффект:
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
//output
java.lang.Object@144
java.lang.Object@145
java.lang.Object@146
java.lang.Object@147
java.lang.Object@148
java.lang.Object@149
Это действительно самовозрастает... немного интересно
Алгоритм 4
if (hashCode == 4) {
value = intptr_t(obj) ;
}
Между этим и первым алгоритмом нет большой разницы, оба возвращают адрес объекта, но первый алгоритм является вариантом.
5-й алгоритм
Последний,Также алгоритм генерации по умолчанию, этот алгоритм используется, когда конфигурация hashCode не равна 0/1/2/3/4:
else {
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
Вот хеш-значение, полученное операцией XOR над текущим значением состояния.По сравнению с предыдущим алгоритмом самоинкремента и случайным алгоритмом, он более эффективен, но частота повторения также должна относительно увеличиваться, но что такое повторение hashCode?Что про отношения...
Изначально jvm не гарантирует, что это значение не должно повторяться.Например, для решения хеш-конфликтов используется метод цепочка адресов в HashMap.
Суммировать
hashCode может быть адресом памяти или нет, или даже константой 1 или автоматически увеличивающимся числом! Вы можете использовать любой алгоритм, какой хотите!
Нелегко быть оригинальным, и несанкционированная перепечатка запрещена. Если моя статья полезна для вас, пожалуйста, поставьте лайк/добавьте в избранное/подпишитесь, чтобы поддержать и поддержать ее ❤❤❤❤❤❤