2015-10-07 1 views
5

Я экспериментировал с портом Java some C# code, и я был удивлен, увидев, что javac 1.8.0_60 выдавал код операции getfield каждый раз, когда к объекту обращались данные.Будет ли законным компилятор Java опустить коды операций getfield после первого доступа?

Вот код Java:

public class BigInteger 
{ 
    private int[] bits; 
    private int sign; 

    //... 

    public byte[] ToByteArray() 
    { 
     if (sign == 0) 
     { 
      return new byte[] { 0 }; 
     } 

     byte highByte; 
     int nonZeroDwordIndex = 0; 
     int highDword; 
     if (bits == null) 
     { 
      highByte = (byte)((sign < 0) ? 0xff : 0x00); 
      highDword = sign; 
     } 
     else if (sign == -1) 
     { 
      highByte = (byte)0xff; 
      assert bits.length > 0; 
      assert bits[bits.length - 1] != 0; 
      while (bits[nonZeroDwordIndex] == 0) 
      { 
       nonZeroDwordIndex++; 
      } 

      highDword = ~bits[bits.length - 1]; 
      if (bits.length - 1 == nonZeroDwordIndex) 
      { 
       highDword += 1; 
      } 
     } 
     else 
     { 
      assert sign == 1; 
      highByte = 0x00; 
      highDword = bits[bits.length - 1]; 
     } 

     byte msb; 
     int msbIndex; 
     if ((msb = (byte)(highDword >>> 24)) != highByte) 
     { 
      msbIndex = 3; 
     } 
     else if ((msb = (byte)(highDword >>> 16)) != highByte) 
     { 
      msbIndex = 2; 
     } 
     else if ((msb = (byte)(highDword >>> 8)) != highByte) 
     { 
      msbIndex = 1; 
     } 
     else 
     { 
      msb = (byte)highDword; 
      msbIndex = 0; 
     } 

     boolean needExtraByte = (msb & 0x80) != (highByte & 0x80); 
     byte[] bytes; 
     int curByte = 0; 
     if (bits == null) 
     { 
      bytes = new byte[msbIndex + 1 + (needExtraByte ? 1 : 0)]; 
      assert bytes.length <= 4; 
     } 
     else 
     { 
      bytes = new byte[4 * (bits.length - 1) + msbIndex + 1 + (needExtraByte ? 1 : 0)]; 

      for (int i = 0; i < bits.length - 1; i++) 
      { 
       int dword = bits[i]; 
       if (sign == -1) 
       { 
        dword = ~dword; 
        if (i <= nonZeroDwordIndex) 
        { 
         dword = dword + 1; 
        } 
       } 
       for (int j = 0; j < 4; j++) 
       { 
        bytes[curByte++] = (byte)dword; 
        dword >>>= 8; 
       } 
      } 
     } 
     for (int j = 0; j <= msbIndex; j++) 
     { 
      bytes[curByte++] = (byte)highDword; 
      highDword >>>= 8; 
     } 
     if (needExtraByte) 
     { 
      bytes[bytes.length - 1] = highByte; 
     } 
     return bytes; 
    } 
} 

Как сообщает javap, Javac 1.8.0_60 производит следующие байты-код:

 
    public byte[] ToByteArray(); 
    Code: 
     0: aload_0 
     1: getfield  #3     // Field sign:I 
     4: ifne   15 
     7: iconst_1 
     8: newarray  byte 
     10: dup 
     11: iconst_0 
     12: iconst_0 
     13: bastore 
     14: areturn 
     15: iconst_0 
     16: istore_2 
     17: aload_0 
     18: getfield  #2     // Field bits:[I 
     21: ifnonnull  48 
     24: aload_0 
     25: getfield  #3     // Field sign:I 
     28: ifge   37 
     31: sipush  255 
     34: goto   38 
     37: iconst_0 
     38: i2b 
     39: istore_1 
     40: aload_0 
     41: getfield  #3     // Field sign:I 
     44: istore_3 
     45: goto   193 
     48: aload_0 
     49: getfield  #3     // Field sign:I 
     52: iconst_m1 
     53: if_icmpne  156 
     56: iconst_m1 
     57: istore_1 
     58: getstatic  #11     // Field $assertionsDisabled:Z 
     61: ifne   80 
     64: aload_0 
     65: getfield  #2     // Field bits:[I 
     68: arraylength 
     69: ifgt   80 
     72: new   #12     // class java/lang/AssertionError 
     75: dup 
     76: invokespecial #13     // Method java/lang/AssertionError."":()V 
     79: athrow 
     80: getstatic  #11     // Field $assertionsDisabled:Z 
     83: ifne   109 
     86: aload_0 
     87: getfield  #2     // Field bits:[I 
     90: aload_0 
     91: getfield  #2     // Field bits:[I 
     94: arraylength 
     95: iconst_1 
     96: isub 
     97: iaload 
     98: ifne   109 
    101: new   #12     // class java/lang/AssertionError 
    104: dup 
    105: invokespecial #13     // Method java/lang/AssertionError."":()V 
    108: athrow 
    109: aload_0 
    110: getfield  #2     // Field bits:[I 
    113: iload_2 
    114: iaload 
    115: ifne   124 
    118: iinc   2, 1 
    121: goto   109 
    124: aload_0 
    125: getfield  #2     // Field bits:[I 
    128: aload_0 
    129: getfield  #2     // Field bits:[I 
    132: arraylength 
    133: iconst_1 
    134: isub 
    135: iaload 
    136: iconst_m1 
    137: ixor 
    138: istore_3 
    139: aload_0 
    140: getfield  #2     // Field bits:[I 
    143: arraylength 
    144: iconst_1 
    145: isub 
    146: iload_2 
    147: if_icmpne  193 
    150: iinc   3, 1 
    153: goto   193 
    156: getstatic  #11     // Field $assertionsDisabled:Z 
    159: ifne   178 
    162: aload_0 
    163: getfield  #3     // Field sign:I 
    166: iconst_1 
    167: if_icmpeq  178 
    170: new   #12     // class java/lang/AssertionError 
    173: dup 
    174: invokespecial #13     // Method java/lang/AssertionError."":()V 
    177: athrow 
    178: iconst_0 
    179: istore_1 
    180: aload_0 
    181: getfield  #2     // Field bits:[I 
    184: aload_0 
    185: getfield  #2     // Field bits:[I 
    188: arraylength 
    189: iconst_1 
    190: isub 
    191: iaload 
    192: istore_3 
    193: iload_3 
    194: bipush  24 
    196: iushr 
    197: i2b 
    198: dup 
    199: istore  4 
    201: iload_1 
    202: if_icmpeq  211 
    205: iconst_3 
    206: istore  5 
    208: goto   254 
    211: iload_3 
    212: bipush  16 
    214: iushr 
    215: i2b 
    216: dup 
    217: istore  4 
    219: iload_1 
    220: if_icmpeq  229 
    223: iconst_2 
    224: istore  5 
    226: goto   254 
    229: iload_3 
    230: bipush  8 
    232: iushr 
    233: i2b 
    234: dup 
    235: istore  4 
    237: iload_1 
    238: if_icmpeq  247 
    241: iconst_1 
    242: istore  5 
    244: goto   254 
    247: iload_3 
    248: i2b 
    249: istore  4 
    251: iconst_0 
    252: istore  5 
    254: iload   4 
    256: sipush  128 
    259: iand 
    260: iload_1 
    261: sipush  128 
    264: iand 
    265: if_icmpeq  272 
    268: iconst_1 
    269: goto   273 
    272: iconst_0 
    273: istore  6 
    275: iconst_0 
    276: istore  8 
    278: aload_0 
    279: getfield  #2     // Field bits:[I 
    282: ifnonnull  325 
    285: iload   5 
    287: iconst_1 
    288: iadd 
    289: iload   6 
    291: ifeq   298 
    294: iconst_1 
    295: goto   299 
    298: iconst_0 
    299: iadd 
    300: newarray  byte 
    302: astore  7 
    304: getstatic  #11     // Field $assertionsDisabled:Z 
    307: ifne   443 
    310: aload   7 
    312: arraylength 
    313: iconst_4 
    314: if_icmple  443 
    317: new   #12     // class java/lang/AssertionError 
    320: dup 
    321: invokespecial #13     // Method java/lang/AssertionError."":()V 
    324: athrow 
    325: iconst_4 
    326: aload_0 
    327: getfield  #2     // Field bits:[I 
    330: arraylength 
    331: iconst_1 
    332: isub 
    333: imul 
    334: iload   5 
    336: iadd 
    337: iconst_1 
    338: iadd 
    339: iload   6 
    341: ifeq   348 
    344: iconst_1 
    345: goto   349 
    348: iconst_0 
    349: iadd 
    350: newarray  byte 
    352: astore  7 
    354: iconst_0 
    355: istore  9 
    357: iload   9 
    359: aload_0 
    360: getfield  #2     // Field bits:[I 
    363: arraylength 
    364: iconst_1 
    365: isub 
    366: if_icmpge  443 
    369: aload_0 
    370: getfield  #2     // Field bits:[I 
    373: iload   9 
    375: iaload 
    376: istore  10 
    378: aload_0 
    379: getfield  #3     // Field sign:I 
    382: iconst_m1 
    383: if_icmpne  404 
    386: iload   10 
    388: iconst_m1 
    389: ixor 
    390: istore  10 
    392: iload   9 
    394: iload_2 
    395: if_icmpgt  404 
    398: iload   10 
    400: iconst_1 
    401: iadd 
    402: istore  10 
    404: iconst_0 
    405: istore  11 
    407: iload   11 
    409: iconst_4 
    410: if_icmpge  437 
    413: aload   7 
    415: iload   8 
    417: iinc   8, 1 
    420: iload   10 
    422: i2b 
    423: bastore 
    424: iload   10 
    426: bipush  8 
    428: iushr 
    429: istore  10 
    431: iinc   11, 1 
    434: goto   407 
    437: iinc   9, 1 
    440: goto   357 
    443: iconst_0 
    444: istore  9 
    446: iload   9 
    448: iload   5 
    450: if_icmpgt  474 
    453: aload   7 
    455: iload   8 
    457: iinc   8, 1 
    460: iload_3 
    461: i2b 
    462: bastore 
    463: iload_3 
    464: bipush  8 
    466: iushr 
    467: istore_3 
    468: iinc   9, 1 
    471: goto   446 
    474: iload   6 
    476: ifeq   488 
    479: aload   7 
    481: aload   7 
    483: arraylength 
    484: iconst_1 
    485: isub 
    486: iload_1 
    487: bastore 
    488: aload   7 
    490: areturn 

Обратите внимание, что getfield опкода выброшенного компилятора каждый раз что доступны поля sign и bits.

Чтение §17.4.5, Случается-до порядка, JLS8, я не вижу, почему это необходимо будет испускать getfield OpCode каждый раз sign и bits поля доступ (кроме в первый раз).

Будет ли законным компилятор Java испускать только два кода операции getfield и сохранять видимые тогда значения полей в локальных переменных кадра?

ответ

4

Это не только законно, но, скорее всего, это произойдет после того, как код будет скомпилирован компилятором JIT (подъем данных является одной из доступных оптимизаций).

Например, код ниже:

public class Test { 
    private boolean stop; 

    public static void main(String[] args) throws InterruptedException { 
    Test t = new Test(); 
    new Thread(t::m).start(); 
// Thread.sleep(1000); 
    System.out.println("stop is now true"); 
    t.stop = true; 
    } 

    private void m() { 
    while (!stop); 
    System.out.println("Finished"); 
    } 

} 

заканчивается быстро (на моей машине, по крайней мере). Это не гарантируется, но поскольку поле извлекается каждый раз, есть точка, в которой изменение распространяется и захватывается.

Если я раскомментировать Thread.sleep(1000) однако, программа никогда не заканчивается, потому что JIT имеет достаточно времени, чтобы оптимизировать код и заменить stop на жесткий кодированное значение, т.е. false.

+1

Благодарим вас за ответ. Я попробовал [jitwatch] (https://github.com/AdoptOpenJDK/jitwatch) в программе, которая вызывает ToByteArray() на разных входах, многократно и спящих между прогонами. JIT перекомпилирует метод 4 раза во время выполнения моего теста. В финальной компиляции сборка x86-64 получает значения полей 'sign' и' bits' только один раз. Я также тестировал версию, где я явно использовал локальные переменные. Сборка x86-64 этой версии «locals» также получает значения полей 'sign' и' bits' только один раз. Интересно, что версия «членов» короче 34 байт. –

+0

@JohnM "* Оптимизатор не собирается предполагать, что переменная никогда не изменится, потому что она не на секунду *" => Это не то, что я говорю. 'stop' никогда не изменяется в' m', и это локальная, не изменчивая переменная. Поэтому оптимизатор может законно переписать 'm' как:' boolean localStop = stop; while (! localStop); '(и это так). Если 'stop' был' volatile', это не было бы юридическим изменением. Точка сна заключается в том, чтобы дать достаточное время для JIT, чтобы начать и скомпилировать код. – assylias

Смежные вопросы