我正在尝试使用Java端口some C# code进行测试,但惊讶地发现每次访问对象字段时javac 1.8.0_60都会发出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)字节[] ToByteArray();
代码:
0:aload_0
1:getfield#3//字段符号:I
4:ifne 15
7:iconst_1
8:newarray字节
10:dup
11:iconst_0
12:iconst_0
13:巴斯托
14:阿雷恩特
15:iconst_0
16:istore_2
17:aload_0
18:getfield#2//字段位:[I
21:ifnonnull 48
24:aload_0
25:getfield#3//字段符号:I
28:ifge 37
31:sipush 255
34:转到38
37:iconst_0
38:i2b
39:istore_1
40:aload_0
41:getfield#3//字段符号:I
44:istore_3
45:转到193
48:aload_0
49:getfield#3//字段符号:I
52:iconst_m1
53:if_icmpne 156
56:图标
57:istore_1
58:getstatiC#11//字段$ assertionsDisabled:Z
61:ifne 80
64:aload_0
65:getfield#2//字段位:[I
68:数组长度
69:ifgt 80
72:新#12//类java/lang/AssertionError
75:dup
76:invokespecial#13//方法java/lang/AssertionError。“” :()V
79: throw
80:getstatiC#11//字段$ assertionsDisabled:Z
83:ifne 109
86:aload_0
87:getfield#2//字段位:[I
90:aload_0
91:getfield#2//字段位:[I
94:数组长度
95:iconst_1
96:isub
97:iaload
98:ifne 109
101:新#12//类java/lang/AssertionError
104:dup
105:invokespecial#13//方法java/lang/AssertionError。“” :()V
108: throw
109:aload_0
110:getfield#2//字段位:[I
113:iload_2
114:iaload
115:124
118:2号,1号
121:转到109
124:加载_0
125:getfield#2//字段位:[I
128:aload_0
129:getfield#2//字段位:[I
132:数组长度
133:iconst_1
134:isub
第135章
第136章
137:埃克索
138:istore_3
139:加载_0
140:getfield#2//字段位:[I
143:数组长度
144:iconst_1
145:isub
146:iload_2
147:if_icmpne 193
150:iinc 3、1
153:转到193
156:getstatiC#11//字段$ assertionsDisabled:Z
159:伊夫尼178
162:aload_0
163:getfield#3//字段符号:I
166:iconst_1
167:if_icmpeq 178
170:新#12//类java/lang/AssertionError
第173章
174:invokespecial#13//方法java/lang/AssertionError。“” :()V
177: throw
178:iconst_0
179:istore_1
180:aload_0
181:getfield#2//字段位:[I
184:aload_0
185:getfield#2//字段位:[I
188:数组长度
189:iconst_1
190:isub
191:iaload
192:istore_3
193:iload_3
194:妖精24
第196章
197:i2b
198:dup
199:istore 4
201:iload_1
202:if_icmpeq 211
205:iconst_3
206:istore 5
208:转到254
211:iload_3
212:蝴蝶结16
214的岁月
215:i2b
第216章
217:istore 4
219:iload_1
220:if_icmpeq 229
223:iconst_2
224:istore 5
226:转到254
229:iload_3
230:蝴蝶8
232:iushr
233:i2b
第234章
235:istore 4
237:iload_1
238:如果是247
241:iconst_1
242:istore 5
244:转到254
247:iload_3
248:i2b
249:istore 4
251:iconst_0
252:istore 5
254:iload 4
256:sipush 128
259:伊安德
260:iload_1
261:sipush 128
第264章
265:if_icmpeq 272
268:iconsst_1
269:转到273
272:iconst_0
273:istore 6
275:iconst_0
276:istore 8
278:加载_0
279:getfield#2//字段位:[I
282:ifnonnull 325
285:重载5
287:iconst_1
288:iadd
289:iload 6
291:ifeq 298
294:iconst_1
295:转到299
298:iconst_0
299:iadd
300:newarray字节
302:商店7
304:getstatiC#11//字段$ assertionsDisabled:Z
307:ifne 443
310:满载7
312:数组长度
313:iconst_4
314:if_icmple 443
317:新#12//类java/lang/AssertionError
320:dup
321:invokespecial#13//方法java/lang/AssertionError。“” :()V
324: throw
325:iconst_4
326:加载_0
327:getfield#2//字段位:[I
330:数组长度
331:iconst_1
332:isub
333:伊穆尔
334:加载5
336:添加
337:iconst_1
338:添加
339:iload 6
341:ifeq 348
344:iconst_1
345:转到349
348:iconst_0
第349章
350:newarray字节
352:商店7
354:iconst_0
355:istore 9
357:iload 9
359:加载_0
360:getfield#2//字段位:[I
363:数组长度
364:iconsst_1
365:isub
366的:if_icmpge 443
369:加载_0
370:getfield#2//字段位:[I
373:iload 9
第375章
376:istore 10
378:aload_0
379:getfield#3//字段符号:I
第382章
383:如果是404
386:载入10
第388章
第389章
390:istore 10
392:iload 9
394:iload_2
395:if_icmpgt 404
398:iload 10
400:iconst_1
401:添加
402:istore 10
404:iconst_0
405:istore 11
407:iload 11
409:iconst_4
410:if_icmpge 437
413:加载7
415:载入8
417:iinc 8,1
420:iload 10
422:i2b
423:bastore
424:载入10
426:蝴蝶8
第428章
429:istore 10
431:iinc 11,1
434:转到407
437:iinc 9、1
440:转到357
443:iconst_0
444:istore 9
446:载入9
448:重载5
450:if_icmpgt 474
453:满载7
455:iload 8
457:第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:iconst_1
485:isub
486:iload_1
第487章
488:满载7
490:阿雷恩特

请注意,每次访问getfieldsign字段时,编译器都会发出bits操作码。

阅读JLS8的第17.4.5节“事前发生”,我不明白为什么每次访问getfieldsign字段(不是第一次)时都需要发出bits操作码。

Java编译器仅发出两个getfield操作码并将字段的当时可见值保存在框架局部变量中是否合法?

最佳答案

这不仅合法,而且一旦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

10-08 06:39