Как генерируется значение hashCode? Адрес памяти объекта?

Java задняя часть

Это пятый день, когда я участвую в 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 или автоматически увеличивающимся числом! Вы можете использовать любой алгоритм, какой хотите!

Нелегко быть оригинальным, и несанкционированная перепечатка запрещена. Если моя статья полезна для вас, пожалуйста, поставьте лайк/добавьте в избранное/подпишитесь, чтобы поддержать и поддержать ее ❤❤❤❤❤❤