我正在试验一些 C# 代码的 Java 端口,我惊讶地发现 javac 1.8.0_60getfield
每次访问对象字段时都会发出一个操作码。
这是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:iconst_1 8:新数组字节 10:重复 11:iconst_0 12:iconst_0 13:巴斯托 14:返回 15:iconst_0 16:istore_2 17:加载_0 18: getfield #2 // 字段位:[I 21:如果非空 48 24:加载_0 25: getfield #3 // 字段符号:I 28:如果 37 31:啜饮 255 34:转到 38 37:图标st_0 38:i2b 39:istore_1 40:加载_0 41: getfield #3 // 字段符号:I 44:istore_3 45:转到 193 48:加载_0 49: getfield #3 // 字段符号:I 52:图标st_m1 53: if_icmpne 156 56:图标st_m1 57:istore_1 58: getstatic #11 // 字段 $assertionsDisabled:Z 61:如果 80 64:加载_0 65: getfield #2 // 字段位:[I 68:数组长度 69:如果 80 72: new #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:加载_0 91: getfield #2 // 字段位:[I 94:数组长度 95:图标st_1 96:伊苏 97:加载 98:如果 109 101: new #12 // 类 java/lang/AssertionError 104:重复 105: invokespecial #13 // 方法 java/lang/AssertionError."":()V 108:扔 109:加载_0 110: getfield #2 // 字段位:[I 113:加载_2 114:加载 115:如果 124 118:iinc 2、1 121:转到109 124:加载_0 125: getfield #2 // 字段位:[I 128:加载_0 129: getfield #2 // 字段位:[I 132:数组长度 133:图标st_1 第134话 135:加载 136:图标st_m1 第137话 138:istore_3 139:加载_0 140: getfield #2 // 字段位:[I 143:数组长度 144:图标st_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:图标st_1 167: if_icmpeq 178 170: new #12 // 类 java/lang/AssertionError 173:重复 174: invokespecial #13 // 方法 java/lang/AssertionError."":()V 177:扔 178:图标st_0 179:istore_1 180:加载_0 181: getfield #2 // 字段位:[I 184:加载_0 185: getfield #2 // 字段位:[I 188:数组长度 189:图标st_1 190:伊苏 191:加载 192:istore_3 193:iload_3 194:双推 24 第196话 197:i2b 198:重复 199:istore 4 201:iload_1 202:if_icmpeq 211 205:图标st_3 206:istore 5 208:转到254 211:加载_3 212:双推 16 第214话 215:i2b 216:重复 217:istore 4 219:iload_1 220:if_icmpeq 229 223:图标st_2 224:istore 5 226:转到254 229:iload_3 230:双向推 8 第232话 233:i2b 234:重复 235:istore 4 237:iload_1 238:如果_icmpeq 247 241:图标st_1 242:istore 5 244:转到254 247:iload_3 248:i2b 249:istore 4 251:图标st_0 252:istore 5 254:加载4 256:吸128 第259话 260:iload_1 261:吸128 第264话 265:如果_icmpeq 272 268:图标st_1 269:转到273 272:图标st_0 273:istore 6 275:图标st_0 276:istore 8 278:加载_0 279: getfield #2 // 字段位:[I 282:如果非空 325 285:加载5 287:图标st_1 288:我加 289:加载6 291:ifeq 298 294:图标st_1 295:转到299 298:图标st_0 299:我加 300:新数组字节 302:商店 7 304: getstatic #11 // 字段 $assertionsDisabled:Z 307:如果 443 310:加载 7 312:数组长度 313:图标st_4 第314章 443 317: 新 #12 // 类 java/lang/AssertionError 320:重复 321: invokespecial #13 // 方法 java/lang/AssertionError."":()V 324:扔 325:图标st_4 326:加载_0 327: getfield #2 // 字段位:[I 330:数组长度 331:图标st_1 第332话 第333话 334:加载 5 336:我加 337:图标st_1 第338话 339:加载6 341:ifeq 348 344:图标st_1 345:转到349 348:图标st_0 349:我加 350:新数组字节 352:商店 7 354:图标st_0 355:istore 9 357:加载 9 359:加载_0 360: getfield #2 // 字段位:[I 363:数组长度 364:图标st_1 365:伊苏 第366章 443 369:加载_0 370: getfield #2 // 字段位:[I 373:加载 9 第375话 376:istore 10 378:加载_0 379: getfield #3 // 字段符号:I 382:图标st_m1 383:if_icmpne 404 386:加载 10 388:图标st_m1 第389话 390:istore 10 392:加载 9 394:iload_2 395:如果_icmpgt 404 398:加载 10 400:图标st_1 401:添加 402:istore 10 404:图标st_0 405:istore 11 407:加载 11 409:图标st_4 410:如果_icmpge 437 413:加载 7 415:加载 8 417:iinc 8、1 420:加载 10 422:i2b 423:巴斯托 424:加载 10 426:双向推 8 第428话 429:istore 10 431:iinc 11、1 434:转到407 437:iinc 9、1 440:转到 357 443:图标st_0 444:istore 9 446:加载 9 448:加载 5 450: if_icmpgt 474 453:加载 7 455:加载 8 457:iinc 8、1 460:iload_3 461:i2b 462:巴斯托 463:iload_3 464:双向推 8 第466话 467:istore_3 468:iinc 9、1 471:转到446 474:加载6 476:ifeq 488 479:加载 7 481:加载 7 483:数组长度 484:图标st_1 第485话 486:iload_1 487:巴斯托 488:加载 7 490:返回
请注意,getfield
每次访问sign
andbits
字段时,编译器都会发出一个操作码。
阅读 JLS8 的§17.4.5, Happens-before Order, 我不明白为什么getfield
每次访问sign
andbits
字段时都需要发出操作码(第一次除外)。
Java 编译器只发出两个getfield
操作码并将当时可见的字段值保存在帧局部变量中是否合法?