这是关于Java优化的一个非常基本的问题。
如果您有一个简单的 for 循环来遍历数组并在循环的标头中使用 array.length 而不是之前对其进行评估,以便您只执行一次(这是我几乎总是这样做的):
for(int i=0; i<array.length;i++) { ... }
是否可以优化语句,以便 JVM 知道数组在循环期间是否正在更改,以便它不会array.length
每次都重新评估?
这是关于Java优化的一个非常基本的问题。
如果您有一个简单的 for 循环来遍历数组并在循环的标头中使用 array.length 而不是之前对其进行评估,以便您只执行一次(这是我几乎总是这样做的):
for(int i=0; i<array.length;i++) { ... }
是否可以优化语句,以便 JVM 知道数组在循环期间是否正在更改,以便它不会array.length
每次都重新评估?
如果另一个线程没有同时修改数组,array.length 将只被有效评估一次,
更关键的是,除非该字段是volatile
JVM,否则无论它是否正确都会做出这个假设。
Java中的数组是固定长度的。数组创建后长度不能更改。首先获取长度并将其分配给局部变量没有任何好处。
在花了太多时间调试这个之后,我得到了:
A:
static String test(String[] arg){
String a ="";
for(int i = 0, len = arg.length; i < len; i++)
if (arg[i].length() > a.length()) a = arg[i];
return a;
}
乙:
static String test(String[] arg){
String a ="";
for(int i = 0; i < arg.length; i++)
if (arg[i].length() > a.length()) a = arg[i];
return a;
}
一个字节码:
static java.lang.String test(java.lang.String[]);
Code:
0: ldc #2; //String
2: astore_1
3: iconst_0
4: istore_2
5: aload_0
6: arraylength
7: istore_3
8: iload_2
9: iload_3
10: if_icmpge 36
13: aload_0
14: iload_2
15: aaload
16: invokevirtual #3; //Method java/lang/String.length:()I
19: aload_1
20: invokevirtual #3; //Method java/lang/String.length:()I
23: if_icmple 30
26: aload_0
27: iload_2
28: aaload
29: astore_1
30: iinc 2, 1
33: goto 8
36: aload_1
37: areturn
B字节码:
static java.lang.String test(java.lang.String[]);
Code:
0: ldc #2; //String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: aload_0
7: arraylength
8: if_icmpge 34
11: aload_0
12: iload_2
13: aaload
14: invokevirtual #3; //Method java/lang/String.length:()I
17: aload_1
18: invokevirtual #3; //Method java/lang/String.length:()I
21: if_icmple 28
24: aload_0
25: iload_2
26: aaload
27: astore_1
28: iinc 2, 1
31: goto 5
34: aload_1
35: areturn
重要的区别是第 33/31 行,其中 goto 跳转到第 8 行或第 5 行。在 (A) 和 (B) 调用 arraylength 之后。
因此,如果不缓存长度,字节码实际上会在每次迭代中调用 arraylength。
当然 javac 不会优化,只有 JIT 会。那么 JIT 是做什么的呢?
通常看起来什么都没有。它未列在 -XX:+PrintCompilation 已编译方法列表中。
只有在调用该函数 100 万次之后,它才会通过展开循环完全激活并删除长度检查,如果我没看错的话:
一个拆机:
# parm0: rsi:rsi = '[Ljava/lang/String;'
# [sp+0x30] (sp of caller)
0x00007fdc296b30a0: mov %eax,-0x6000(%rsp)
0x00007fdc296b30a7: push %rbp
0x00007fdc296b30a8: sub $0x20,%rsp ;*synchronization entry
; - Acminesimple::test@-1 (line 3)
0x00007fdc296b30ac: mov 0xc(%rsi),%ebp ;*arraylength
; - Acminesimple::test@6 (line 4)
; implicit exception: dispatches to 0x00007fdc296b31ad
0x00007fdc296b30af: movabs $0xeb8b7168,%rax ; {oop("")}
0x00007fdc296b30b9: test %ebp,%ebp
0x00007fdc296b30bb: jle 0x00007fdc296b312e ;*if_icmpge
; - Acminesimple::test@10 (line 4)
0x00007fdc296b30bd: test %ebp,%ebp
0x00007fdc296b30bf: jbe 0x00007fdc296b3177
0x00007fdc296b30c5: mov %ebp,%ecx
0x00007fdc296b30c7: dec %ecx
0x00007fdc296b30c9: cmp %ebp,%ecx
0x00007fdc296b30cb: jae 0x00007fdc296b3177
0x00007fdc296b30d1: xor %r8d,%r8d ;*aload_0
; - Acminesimple::test@13 (line 5)
0x00007fdc296b30d4: mov 0x10(%rsi,%r8,4),%edi ;*aaload
; - Acminesimple::test@15 (line 5)
0x00007fdc296b30d9: mov 0x14(%rdi),%r11d ; implicit exception: dispatches to 0x00007fdc296b318d
0x00007fdc296b30dd: mov 0x14(%rax),%r10d ; implicit exception: dispatches to 0x00007fdc296b319d
0x00007fdc296b30e1: cmp %r10d,%r11d
0x00007fdc296b30e4: jg 0x00007fdc296b30e9 ;*if_icmple
; - Acminesimple::test@23 (line 5)
0x00007fdc296b30e6: mov %rax,%rdi
0x00007fdc296b30e9: inc %r8d ;*iinc
; - Acminesimple::test@30 (line 4)
0x00007fdc296b30ec: cmp $0x1,%r8d
0x00007fdc296b30f0: jge 0x00007fdc296b30f7 ;*if_icmpge
; - Acminesimple::test@10 (line 4)
0x00007fdc296b30f2: mov %rdi,%rax ;*iinc
; - Acminesimple::test@30 (line 4)
0x00007fdc296b30f5: jmp 0x00007fdc296b30d4
0x00007fdc296b30f7: cmp %ecx,%r8d
0x00007fdc296b30fa: jl 0x00007fdc296b3163
0x00007fdc296b30fc: mov %edi,%r10d
0x00007fdc296b30ff: cmp %ebp,%r8d
0x00007fdc296b3102: jl 0x00007fdc296b310f
0x00007fdc296b3104: mov %r10d,%r9d
0x00007fdc296b3107: jmp 0x00007fdc296b312b
0x00007fdc296b3109: data32 xchg %ax,%ax
0x00007fdc296b310c: mov %r9d,%r10d ;*aload_0
; - Acminesimple::test@13 (line 5)
0x00007fdc296b310f: mov 0x10(%rsi,%r8,4),%r9d ;*aaload
; - Acminesimple::test@15 (line 5)
0x00007fdc296b3114: mov 0x14(%r9),%r11d ; implicit exception: dispatches to 0x00007fdc296b318d
0x00007fdc296b3118: mov 0x14(%r10),%ebx ; implicit exception: dispatches to 0x00007fdc296b319d
0x00007fdc296b311c: cmp %ebx,%r11d
0x00007fdc296b311f: cmovle %r10d,%r9d
0x00007fdc296b3123: inc %r8d ;*iinc
; - Acminesimple::test@30 (line 4)
0x00007fdc296b3126: cmp %ebp,%r8d
0x00007fdc296b3129: jl 0x00007fdc296b310c
0x00007fdc296b312b: mov %r9,%rax ;*if_icmpge
; - Acminesimple::test@10 (line 4)
0x00007fdc296b312e: add $0x20,%rsp
0x00007fdc296b3132: pop %rbp
0x00007fdc296b3133: test %eax,0x5766ec7(%rip) # 0x00007fdc2ee1a000
; {poll_return}
0x00007fdc296b3139: retq
0x00007fdc296b313a: mov %r11d,%edi
0x00007fdc296b313d: data32 xchg %ax,%ax ;*iinc
; - Acminesimple::test@30 (line 4)
0x00007fdc296b3140: movslq %r8d,%r10
0x00007fdc296b3143: mov 0x14(%rsi,%r10,4),%r10d ;*aaload
; - Acminesimple::test@15 (line 5)
0x00007fdc296b3148: mov 0x14(%r10),%r9d ; implicit exception: dispatches to 0x00007fdc296b318d
0x00007fdc296b314c: mov 0x14(%rdi),%r11d ; implicit exception: dispatches to 0x00007fdc296b319d
0x00007fdc296b3150: cmp %r11d,%r9d
0x00007fdc296b3153: cmovle %edi,%r10d
0x00007fdc296b3157: add $0x2,%r8d ;*iinc
; - Acminesimple::test@30 (line 4)
0x00007fdc296b315b: cmp %ecx,%r8d
0x00007fdc296b315e: jge 0x00007fdc296b30ff ;*if_icmpge
; - Acminesimple::test@10 (line 4)
0x00007fdc296b3160: mov %r10d,%edi ;*aload_0
; - Acminesimple::test@13 (line 5)
0x00007fdc296b3163: mov 0x10(%rsi,%r8,4),%r11d ;*aaload
; - Acminesimple::test@15 (line 5)
0x00007fdc296b3168: mov 0x14(%r11),%r10d ; implicit exception: dispatches to 0x00007fdc296b318d
0x00007fdc296b316c: mov 0x14(%rdi),%r9d ; implicit exception: dispatches to 0x00007fdc296b319d
0x00007fdc296b3170: cmp %r9d,%r10d
; - Acminesimple::test@23 (line 5)
0x00007fdc296b3175: jmp 0x00007fdc296b3140
0x00007fdc296b3177: mov %rsi,(%rsp)
0x00007fdc296b317b: mov $0xffffff86,%esi
0x00007fdc296b3180: data32 xchg %ax,%ax
0x00007fdc296b3183: callq 0x00007fdc29620320 ; OopMap{[0]=Oop off=232}
;*aload_0
; - Acminesimple::test@13 (line 5)
; {runtime_call}
0x00007fdc296b3188: callq 0x00007fdc2de8b7c0 ;*aload_0
; - Acminesimple::test@13 (line 5)
; {runtime_call}
0x00007fdc296b318d: mov $0xfffffff6,%esi
0x00007fdc296b3192: nop
0x00007fdc296b3193: callq 0x00007fdc29620320 ; OopMap{off=248}
;*invokevirtual length
; - Acminesimple::test@16 (line 5)
; {runtime_call}
0x00007fdc296b3198: callq 0x00007fdc2de8b7c0 ;*invokevirtual length
; - Acminesimple::test@16 (line 5)
; {runtime_call}
0x00007fdc296b319d: mov $0xfffffff6,%esi
0x00007fdc296b31a2: nop
0x00007fdc296b31a3: callq 0x00007fdc29620320 ; OopMap{off=264}
;*invokevirtual length
; - Acminesimple::test@20 (line 5)
; {runtime_call}
0x00007fdc296b31a8: callq 0x00007fdc2de8b7c0 ;*invokevirtual length
; - Acminesimple::test@20 (line 5)
; {runtime_call}
0x00007fdc296b31ad: mov $0xfffffff6,%esi
0x00007fdc296b31b2: nop
0x00007fdc296b31b3: callq 0x00007fdc29620320 ; OopMap{off=280}
;*arraylength
; - Acminesimple::test@6 (line 4)
; {runtime_call}
0x00007fdc296b31b8: callq 0x00007fdc2de8b7c0 ;*arraylength
; - Acminesimple::test@6 (line 4)
; {runtime_call}
0x00007fdc296b31bd: hlt
0x00007fdc296b31be: hlt
0x00007fdc296b31bf: hlt
[Exception Handler]
B拆解:
# parm0: rsi:rsi = '[Ljava/lang/String;'
# [sp+0x20] (sp of caller)
0x00007fc749ebcf20: mov %eax,-0x6000(%rsp)
0x00007fc749ebcf27: push %rbp
0x00007fc749ebcf28: sub $0x10,%rsp ;*synchronization entry
; - Acfoamsimple::test@-1 (line 3)
0x00007fc749ebcf2c: mov 0xc(%rsi),%r9d ;*arraylength
; - Acfoamsimple::test@7 (line 4)
; implicit exception: dispatches to 0x00007fc749ebd029
0x00007fc749ebcf30: movabs $0xeb8b7168,%rax ; {oop("")}
0x00007fc749ebcf3a: test %r9d,%r9d
0x00007fc749ebcf3d: jle 0x00007fc749ebcfad ;*if_icmpge
; - Acfoamsimple::test@8 (line 4)
0x00007fc749ebcf3f: test %r9d,%r9d
0x00007fc749ebcf42: jbe 0x00007fc749ebcff5
0x00007fc749ebcf48: mov %r9d,%ebx
0x00007fc749ebcf4b: dec %ebx
0x00007fc749ebcf4d: cmp %r9d,%ebx
0x00007fc749ebcf50: jae 0x00007fc749ebcff5
0x00007fc749ebcf56: xor %ecx,%ecx ;*aload_0
; - Acfoamsimple::test@11 (line 5)
0x00007fc749ebcf58: mov 0x10(%rsi,%rcx,4),%edx ;*aaload
; - Acfoamsimple::test@13 (line 5)
0x00007fc749ebcf5c: mov 0x14(%rdx),%r10d ; implicit exception: dispatches to 0x00007fc749ebd009
0x00007fc749ebcf60: mov 0x14(%rax),%r8d ; implicit exception: dispatches to 0x00007fc749ebd019
0x00007fc749ebcf64: cmp %r8d,%r10d
0x00007fc749ebcf67: jg 0x00007fc749ebcf6c ;*if_icmple
; - Acfoamsimple::test@21 (line 5)
0x00007fc749ebcf69: mov %rax,%rdx
0x00007fc749ebcf6c: inc %ecx ;*iinc
; - Acfoamsimple::test@28 (line 4)
0x00007fc749ebcf6e: cmp $0x1,%ecx
0x00007fc749ebcf71: jge 0x00007fc749ebcf78 ;*if_icmpge
; - Acfoamsimple::test@8 (line 4)
0x00007fc749ebcf73: mov %rdx,%rax ;*iinc
; - Acfoamsimple::test@28 (line 4)
0x00007fc749ebcf76: jmp 0x00007fc749ebcf58
0x00007fc749ebcf78: cmp %ebx,%ecx
0x00007fc749ebcf7a: jl 0x00007fc749ebcfe1
0x00007fc749ebcf7c: mov %edx,%r10d
0x00007fc749ebcf7f: cmp %r9d,%ecx
0x00007fc749ebcf82: jl 0x00007fc749ebcf8f
0x00007fc749ebcf84: mov %r10d,%r8d
0x00007fc749ebcf87: jmp 0x00007fc749ebcfaa
0x00007fc749ebcf89: data32 xchg %ax,%ax
0x00007fc749ebcf8c: mov %r8d,%r10d ;*aload_0
; - Acfoamsimple::test@11 (line 5)
0x00007fc749ebcf8f: mov 0x10(%rsi,%rcx,4),%r8d ;*aaload
; - Acfoamsimple::test@13 (line 5)
0x00007fc749ebcf94: mov 0x14(%r8),%r11d ; implicit exception: dispatches to 0x00007fc749ebd009
0x00007fc749ebcf98: mov 0x14(%r10),%edi ; implicit exception: dispatches to 0x00007fc749ebd019
0x00007fc749ebcf9c: cmp %edi,%r11d
0x00007fc749ebcf9f: cmovle %r10d,%r8d
0x00007fc749ebcfa3: inc %ecx ;*iinc
; - Acfoamsimple::test@28 (line 4)
0x00007fc749ebcfa5: cmp %r9d,%ecx
0x00007fc749ebcfa8: jl 0x00007fc749ebcf8c
0x00007fc749ebcfaa: mov %r8,%rax ;*if_icmpge
; - Acfoamsimple::test@8 (line 4)
0x00007fc749ebcfad: add $0x10,%rsp
0x00007fc749ebcfb1: pop %rbp
0x00007fc749ebcfb2: test %eax,0x5766048(%rip) # 0x00007fc74f623000
; {poll_return}
0x00007fc749ebcfb8: retq
0x00007fc749ebcfb9: mov %r10d,%edx
0x00007fc749ebcfbc: nopl 0x0(%rax) ;*iinc
; - Acfoamsimple::test@28 (line 4)
0x00007fc749ebcfc0: movslq %ecx,%r10
0x00007fc749ebcfc3: mov 0x14(%rsi,%r10,4),%r10d ;*aaload
; - Acfoamsimple::test@13 (line 5)
0x00007fc749ebcfc8: mov 0x14(%r10),%r8d ; implicit exception: dispatches to 0x00007fc749ebd009
0x00007fc749ebcfcc: mov 0x14(%rdx),%r11d ; implicit exception: dispatches to 0x00007fc749ebd019
0x00007fc749ebcfd0: cmp %r11d,%r8d
0x00007fc749ebcfd3: cmovle %edx,%r10d
0x00007fc749ebcfd7: add $0x2,%ecx ;*iinc
; - Acfoamsimple::test@28 (line 4)
0x00007fc749ebcfda: cmp %ebx,%ecx
0x00007fc749ebcfdc: jge 0x00007fc749ebcf7f ;*if_icmpge
; - Acfoamsimple::test@8 (line 4)
0x00007fc749ebcfde: mov %r10d,%edx ;*aload_0
; - Acfoamsimple::test@11 (line 5)
0x00007fc749ebcfe1: mov 0x10(%rsi,%rcx,4),%r10d ;*aaload
; - Acfoamsimple::test@13 (line 5)
0x00007fc749ebcfe6: mov 0x14(%r10),%r8d ; implicit exception: dispatches to 0x00007fc749ebd009
0x00007fc749ebcfea: mov 0x14(%rdx),%r11d ; implicit exception: dispatches to 0x00007fc749ebd019
0x00007fc749ebcfee: cmp %r11d,%r8d
0x00007fc749ebcff1: jg 0x00007fc749ebcfb9 ;*if_icmple
; - Acfoamsimple::test@21 (line 5)
0x00007fc749ebcff3: jmp 0x00007fc749ebcfc0
0x00007fc749ebcff5: mov %rsi,%rbp
0x00007fc749ebcff8: mov $0xffffff86,%esi
0x00007fc749ebcfff: callq 0x00007fc749e29320 ; OopMap{rbp=Oop off=228}
;*aload_0
; - Acfoamsimple::test@11 (line 5)
; {runtime_call}
0x00007fc749ebd004: callq 0x00007fc74e6947c0 ;*aload_0
; - Acfoamsimple::test@11 (line 5)
; {runtime_call}
0x00007fc749ebd009: mov $0xfffffff6,%esi
0x00007fc749ebd00e: nop
0x00007fc749ebd00f: callq 0x00007fc749e29320 ; OopMap{off=244}
;*invokevirtual length
; - Acfoamsimple::test@14 (line 5)
; {runtime_call}
0x00007fc749ebd014: callq 0x00007fc74e6947c0 ;*invokevirtual length
; - Acfoamsimple::test@14 (line 5)
; {runtime_call}
0x00007fc749ebd019: mov $0xfffffff6,%esi
0x00007fc749ebd01e: nop
0x00007fc749ebd01f: callq 0x00007fc749e29320 ; OopMap{off=260}
;*invokevirtual length
; - Acfoamsimple::test@18 (line 5)
; {runtime_call}
0x00007fc749ebd024: callq 0x00007fc74e6947c0 ;*invokevirtual length
; - Acfoamsimple::test@18 (line 5)
; {runtime_call}
0x00007fc749ebd029: mov $0xfffffff6,%esi
0x00007fc749ebd02e: nop
0x00007fc749ebd02f: callq 0x00007fc749e29320 ; OopMap{off=276}
;*arraylength
; - Acfoamsimple::test@7 (line 4)
; {runtime_call}
0x00007fc749ebd034: callq 0x00007fc74e6947c0 ;*arraylength
; - Acfoamsimple::test@7 (line 4)
; {runtime_call}
0x00007fc749ebd039: hlt
0x00007fc749ebd03a: hlt
0x00007fc749ebd03b: hlt
0x00007fc749ebd03c: hlt
0x00007fc749ebd03d: hlt
0x00007fc749ebd03e: hlt
0x00007fc749ebd03f: hlt
在讨论开始的问题中使用Math.min(args.length, 20)
而不是args.length
like ,它的行为类似:
A-with-min:
static java.lang.String test(java.lang.String[]);
Code:
0: ldc #2; //String
2: astore_1
3: iconst_0
4: istore_2
5: aload_0
6: arraylength
7: bipush 20
9: invokestatic #3; //Method java/lang/Math.min:(II)I
12: istore_3
13: iload_2
14: iload_3
15: if_icmpge 41
18: aload_0
19: iload_2
20: aaload
21: invokevirtual #4; //Method java/lang/String.length:()I
24: aload_1
25: invokevirtual #4; //Method java/lang/String.length:()I
28: if_icmple 35
31: aload_0
32: iload_2
33: aaload
34: astore_1
35: iinc 2, 1
38: goto 13
41: aload_1
42: areturn
B-with-min:
static java.lang.String test(java.lang.String[]);
Code:
0: ldc #2; //String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: aload_0
7: arraylength
8: bipush 20
10: invokestatic #3; //Method java/lang/Math.min:(II)I
13: if_icmpge 39
16: aload_0
17: iload_2
18: aaload
19: invokevirtual #4; //Method java/lang/String.length:()I
22: aload_1
23: invokevirtual #4; //Method java/lang/String.length:()I
26: if_icmple 33
29: aload_0
30: iload_2
31: aaload
32: astore_1
33: iinc 2, 1
36: goto 5
39: aload_1
40: areturn
(jit 反汇编函数太长,无法在答案中发布)