Переупорядочивание инструкций в Java JVM

я читалэтот Сообщение блога.

И автор говорил о взломеhashCode() вString в многопоточной среде.

Имея:

public int hashCode() {
     int h = hash;
     if (h == 0) {
         int off = offset;
         char val[] = value;
         int len = count;

         for (int i = 0; i < len; i++) {
             h = 31*h + val[off++];
         }
         hash = h;
     }
     return h;
 }

Изменился на:

public int hashCode() {
     if (hash == 0) {
         int off = offset;
         char val[] = value;
         int len = count;

         int h = 0;
         for (int i = 0; i < len; i++) {
             h = 31*h + val[off++];
         }
         hash = h;
     }
     return hash;
 }

Который автор говорит, и я цитирую:

"What I've done here is to add an additional read: the second read of hash, before the return. As odd as it sounds, and as unlikely as it is to happen, the first read can return the correctly computed hash value, and the second read can return 0! This is allowed under the memory model because the model allows extensive reordering of operations. The second read can actually be moved, in your code, so that your processor does it before the first!"

Далее, просматривая комментарии, кто-то говорит, что его можно переупорядочить

int h = hash;
if (hash == 0) {
  ...
}
return h;

Как это возможно? Я думал, что переупорядочение включает только перемещение программных операторов вверх и вниз. Каким правилам оно следует? Я гуглил, прочитал FAQ по JSR133, проверил книгу «Параллелизм Java на практике», но, похоже, не могу найти место, которое поможет мне понять, в частности, о переупорядочении. Если кто-нибудь может указать мне правильное направление, я был бы очень признателен.

Thanks to Louis clarifying the meaning of "Reordering", I wasn't thinking in terms of "byteCode"

Однако я до сих пор не понимаю, почему разрешено перемещать 2-е чтение вперед, это моя наивная попытка перевести его в несколько «байт-код». формат.

Для упрощения операции, используемые для вычисления хеш-кода, выражаются какcalchash(), Поэтому я выражаю программу как:

if (hash == 0)  {       
    h = calchash();
    hash = h;
}
return hash;

И моя попытка выразить это в «байт-коде» форма:

R1,R2,R3 are in the operands stack, or the registers
h is in the array of local variables

В порядке программы:

if (hash == 0)  {       ---------- R1 = read hash from memory (1st read)
                        ---------- Compare (R1 == 0)
    h = calchash();     ---------- R2 = calchash()
                        ---------- h = R2 (Storing the R2 to local variable h)
    hash = h;           ---------- Hash = h (write to hash)
}
return hash             ---------- R3 = read hash from memory again(2nd read)
                        ---------- return R3

Изменение порядка преобразования (Моя версия основана на комментариях):

                        ---------- R3 = read hash from memory (2nd read) *moved*
if (hash == 0)  {       ---------- R1 = read hash from memory (1st read)
                        ---------- Compare (R1 == 0)
    h = calchash();     ---------- R2 = calchash()
                        ---------- h = R2 (Storing the R2 to local variable h)
    hash = h;           ---------- hash = h (write to hash)
}
return hash             ---------- return R3

Checking the comments again, I found this answered by the author:

Изменение порядка трансформации (из блога)

r1 = hash;
if (hash == 0) {
  r1 = hash = // calculate hash
}
return r1;

Этот случай фактически работает в одном потоке, но возможен сбой в нескольких потоках.

Кажется, что JVM делают упрощения на основе

h = hash and it simplifies the use of R1, R2, R3 to single R1

Таким образом, JVM делает больше, чем просто переупорядочивание команд, и это также уменьшает количество используемых регистров.

Ответы на вопрос(4)

Ваш ответ на вопрос