私はC# コードの Java ポートを試していましたgetfield
が、オブジェクト フィールドにアクセスするたびに javac 1.8.0_60 がオペコードを発行していることに驚きました。
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 は次のバイトコードを生成します。
パブリックバイト[] ToByteArray(); コード: 0: aload_0 1: getfield #3 // フィールド記号:I 4: イフネ15 7: アイコンスト_1 8: 新しい配列バイト 10: 重複 11: アイコンスト_0 12: アイコンスト_0 13:バストア 14: 戻る 15: アイコンスト_0 16: istore_2 17: aload_0 18: getfield #2 // フィールド ビット:[I 21: ifnonnull 48 24: アロード_0 25: getfield #3 // フィールド記号:I 28: イフゲ37 31: シプシュ255 34: 後藤 38 37: アイコンスト_0 38:アイツーブ 39: イストア_1 40: アロード_0 41: getfield #3 // フィールド記号:I 44: イストア_3 45: 後藤193 48: アロード_0 49: getfield #3 // フィールド記号:I 52: アイコンスト_m1 53: if_icmpne 156 56: アイコンスト_m1 57: イストア_1 58: getstatic #11 // フィールド $assertionsDisabled:Z 61: イフネ80 64: アロード_0 65: getfield #2 // フィールド ビット:[I 68: 配列の長さ 69: イフト 80 72: 新しい #12 // クラス java/lang/AssertionError 75: デュプ 76: invokespecial #13 // メソッド java/lang/AssertionError."":()V 79: 投げる 80: getstatic #11 // フィールド $assertionsDisabled:Z 83:イフネ109 86: アロード_0 87: getfield #2 // フィールド ビット:[I 90: aload_0 91: getfield #2 // フィールド ビット:[I 94: 配列の長さ 95: アイコンスト_1 96:イサブ 97:イアロード 98: イフネ109 101: 新しい #12 // クラス java/lang/AssertionError 104: デュプ 105: invokespecial #13 // メソッド java/lang/AssertionError."":()V 108: 投げる 109: aload_0 110: getfield #2 // フィールド ビット:[I 113: iload_2 114: イアロード 115: イフネ124 118: イインク 2, 1 121: 後藤109 124: アロード_0 125: getfield #2 // フィールド ビット:[I 128: アロード_0 129: getfield #2 // フィールド ビット:[I 132: 配列の長さ 133: アイコンスト_1 134: イサブ 135: イアロード 136: アイコンスト_m1 137:イクサー 138: イストア_3 139: アロード_0 140: getfield #2 // フィールド ビット:[I 143: 配列の長さ 144: アイコンスト_1 145: イサブ 146: イロード_2 147: if_icmpne 193 150: iinc 3, 1 153: 後藤 193 156: getstatic #11 // フィールド $assertionsDisabled:Z 159: イフネ178 162: アロード_0 163: getfield #3 // フィールド記号:I 166: アイコンスト_1 167: if_icmpeq 178 170: 新しい #12 // クラス java/lang/AssertionError 173: デュプ 174: invokespecial #13 // メソッド java/lang/AssertionError."":()V 177: 投げる 178: アイコンスト_0 179: イストア_1 180: aload_0 181: getfield #2 // フィールド ビット:[I 184: アロード_0 185: getfield #2 // フィールド ビット:[I 188: 配列の長さ 189: アイコンスト_1 190: イサブ 191: イアロード 192: イストア_3 193: iload_3 194:バイプッシュ24 196: イシュル 197: えんとつ 198: デュプ 199: イストア4 201: iload_1 202: if_icmpeq 211 205: アイコンスト_3 206: ストア 5 208: 後藤 254 211: iload_3 212:バイプッシュ16 214: ウシュル 215:アイツーブ 216: デュプ 217: ストア 4 219: iload_1 220: if_icmpeq 229 223: アイコンスト_2 224: イストア5 226: 後藤 254 229: iload_3 230:バイプッシュ8 232: ウシュル 233: えんとつ 234: デュプ 235: ストア 4 237: iload_1 238: if_icmpeq 247 241: アイコンスト_1 242: ストア5 244: 後藤 254 247: iload_3 248: えんとつ 249: イストア4 251: アイコンスト_0 252: ストア5 254: iload 4 256: シプシュ128 259: イアンド 260: iload_1 261: シプシュ128 264: イアンド 265: if_icmpeq 272 268: アイコンスト_1 269: 後藤 273 272: アイコンスト_0 273: イストア6 275: アイコンスト_0 276: イストア8 278: アロード_0 279: getfield #2 // フィールド ビット:[I 282: 325 285: イロード5 287: アイコンスト_1 288: しんしん 289: イロード6 291: イフェク 298 294: アイコンスト_1 295: 後藤 299 298: アイコンスト_0 299: たつ 300: newarray バイト 302: アストア 7 304: getstatic #11 // フィールド $assertionsDisabled:Z 307: イフネ443 310: アロード 7 312: 配列の長さ 313: アイコンスト_4 314: if_icmple 443 317: 新しい #12 // クラス java/lang/AssertionError 320: デュプ 321: invokespecial #13 // メソッド java/lang/AssertionError."":()V 324: 投げる 325: アイコンスト_4 326: アロード_0 327: getfield #2 // フィールド ビット:[I 330: 配列の長さ 331: アイコンスト_1 332: イサブ 333: イムル 334: iload 5 336: しんしん 337: アイコンスト_1 338: しんしん 339: イロード6 341: イフェク 348 344: アイコンスト_1 345: 後藤 349 348: アイコンスト_0 349: しんしん 350: newarray バイト 352:アストア7 354: アイコンスト_0 355: イストア9 357: イロード9 359: アロード_0 360: getfield #2 // フィールド ビット:[I 363: 配列の長さ 364: アイコンスト_1 365: イサブ 366: if_icmpge 443 369: アロード_0 370: getfield #2 // フィールド ビット:[I 373:イロード9 375: イアロード 376: イストア10 378: アロード_0 379: getfield #3 // フィールド記号:I 382: アイコンスト_m1 383: if_icmpne 404 386: イロード10 388: アイコンスト_m1 389:イクサー 390: イストア 10 392:イロード9 394: イロード_2 395: if_icmpgt 404 398:イロード10 400: アイコンスト_1 401: iadd 402: イストア 10 404: アイコンスト_0 405: イストア 11 407: iload 11 409: アイコンスト_4 410: if_icmpge 437 413: アロード 7 415: iload 8 417: イインク 8, 1 420: iload 10 422: えんとつ 423: バストア 424: iload 10 426:バイプッシュ8 428: ウシュル 429: ストア 10 431: イインク 11, 1 434: 407に行く 437: イインク 9, 1 440: 後藤 357 443: アイコンスト_0 444: イストア9 446: イロード9 448: イロード5 450: if_icmpgt 474 453: アロード 7 455: iload 8 457: イインク 8, 1 460: iload_3 461: しんしん 462:バストア 463: iload_3 464:バイプッシュ8 466: イシュル 467: イストア_3 468: イインク 9, 1 471: 後藤446 474: イロード6 476: 488 479: アロード 7 481: アロード 7 483: 配列の長さ 484: アイコンスト_1 485: イサブ 486: iload_1 487:バストア 488: アロード 7 490:リターン
およびフィールドがアクセスされるgetfield
たびに、コンパイラによってオペコードが発行されたことに注意してください。sign
bits
JLS8 の §17.4.5、Happens-before Order を読んで、フィールドとフィールドがアクセスされるgetfield
たびにオペコードを発行する必要がある理由がわかりません(初回以外)。sign
bits
Java コンパイラが 2 つのgetfield
オペコードのみを発行し、その時点で表示されているフィールドの値をフレーム ローカル変数に保存することは合法でしょうか?