Ответ 1
Это оптимизация компилятора JIT (не оптимизация java-компилятора).
Если вы сравниваете байт-код, сгенерированный java-компилятором для двух версий, вы увидите, что цикл присутствует в обоих из них.
Вот как выглядит декомпилированный метод с println:
private static void loop() {
long value = 0L;
for(int j = 0; j < '썐'; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
System.out.println("Done " + value);
}
Вот как выглядит декомпилированный метод при удалении println:
private static void loop() {
long value = 0L;
for(int j = 0; j < '썐'; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
}
Как видите, петли все еще существуют.
Однако вы можете включить ведение журнала компилятора JIT и сборку со следующими параметрами JVM:
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintAssembly
Вам также может понадобиться загрузить hsdis-amd64.dylib и поместить в ваш рабочий каталог (MacOS, HotSpot Java 8)
После запуска TestClient вы должны увидеть код, созданный компилятором JIT в консоли. Здесь я выведу только выдержку из вывода.
Версия без println:
# {method} 'loop' '()V' in 'test/TestClient'
0x000000010e3c2500: callq 0x000000010dc1c202 ; {runtime_call}
0x000000010e3c2505: data32 data32 nopw 0x0(%rax,%rax,1)
0x000000010e3c2510: sub $0x18,%rsp
0x000000010e3c2517: mov %rbp,0x10(%rsp)
0x000000010e3c251c: mov %rsi,%rdi
0x000000010e3c251f: movabs $0x10dc760ec,%r10
0x000000010e3c2529: callq *%r10 ;*iload_3
; - test.TestClient::[email protected] (line 9)
0x000000010e3c252c: add $0x10,%rsp
0x000000010e3c2530: pop %rbp
0x000000010e3c2531: test %eax,-0x1c18537(%rip) # 0x000000010c7aa000
; {poll_return}
0x000000010e3c2537: retq
Версия с println:
# {method} 'loop' '()V' in 'test/TestClient'
0x00000001092c36c0: callq 0x0000000108c1c202 ; {runtime_call}
0x00000001092c36c5: data32 data32 nopw 0x0(%rax,%rax,1)
0x00000001092c36d0: mov %eax,-0x14000(%rsp)
0x00000001092c36d7: push %rbp
0x00000001092c36d8: sub $0x10,%rsp
0x00000001092c36dc: mov 0x10(%rsi),%r13
0x00000001092c36e0: mov 0x8(%rsi),%ebp
0x00000001092c36e3: mov (%rsi),%ebx
0x00000001092c36e5: mov %rsi,%rdi
0x00000001092c36e8: movabs $0x108c760ec,%r10
0x00000001092c36f2: callq *%r10
0x00000001092c36f5: jmp 0x00000001092c3740
0x00000001092c36f7: add $0x1,%r13 ;*iload_3
; - test.TestClient::[email protected] (line 9)
0x00000001092c36fb: inc %ebx ;*iinc
; - test.TestClient::[email protected] (line 9)
0x00000001092c36fd: cmp $0x5f5e101,%ebx
0x00000001092c3703: jl 0x00000001092c36f7 ;*if_icmpge
; - test.TestClient::[email protected] (line 9)
0x00000001092c3705: jmp 0x00000001092c3734
0x00000001092c3707: nopw 0x0(%rax,%rax,1)
0x00000001092c3710: mov %r13,%r8 ;*iload_3
; - test.TestClient::[email protected] (line 9)
0x00000001092c3713: mov %r8,%r13
0x00000001092c3716: add $0x10,%r13 ;*ladd
; - test.TestClient::[email protected] (line 10)
0x00000001092c371a: add $0x10,%ebx ;*iinc
; - test.TestClient::[email protected] (line 9)
0x00000001092c371d: cmp $0x5f5e0f2,%ebx
0x00000001092c3723: jl 0x00000001092c3710 ;*if_icmpge
; - test.TestClient::[email protected] (line 9)
0x00000001092c3725: add $0xf,%r8 ;*ladd
; - test.TestClient::[email protected] (line 10)
0x00000001092c3729: cmp $0x5f5e101,%ebx
0x00000001092c372f: jl 0x00000001092c36fb
0x00000001092c3731: mov %r8,%r13 ;*iload_3
; - test.TestClient::[email protected] (line 9)
0x00000001092c3734: inc %ebp ;*iinc
; - test.TestClient::[email protected] (line 8)
0x00000001092c3736: cmp $0xc350,%ebp
0x00000001092c373c: jge 0x00000001092c376c ;*if_icmpge
; - test.TestClient::[email protected] (line 8)
0x00000001092c373e: xor %ebx,%ebx
0x00000001092c3740: mov %ebx,%r11d
0x00000001092c3743: inc %r11d ;*iload_3
; - test.TestClient::[email protected] (line 9)
0x00000001092c3746: mov %r13,%r8
0x00000001092c3749: add $0x1,%r8 ;*ladd
; - test.TestClient::[email protected] (line 10)
0x00000001092c374d: inc %ebx ;*iinc
; - test.TestClient::[email protected] (line 9)
0x00000001092c374f: cmp %r11d,%ebx
0x00000001092c3752: jge 0x00000001092c3759 ;*if_icmpge
; - test.TestClient::[email protected] (line 9)
0x00000001092c3754: mov %r8,%r13
0x00000001092c3757: jmp 0x00000001092c3746
0x00000001092c3759: cmp $0x5f5e0f2,%ebx
0x00000001092c375f: jl 0x00000001092c3713
0x00000001092c3761: mov %r13,%r10
0x00000001092c3764: mov %r8,%r13
0x00000001092c3767: mov %r10,%r8
0x00000001092c376a: jmp 0x00000001092c3729 ;*if_icmpge
; - test.TestClient::[email protected] (line 8)
0x00000001092c376c: mov $0x24,%esi
0x00000001092c3771: mov %r13,%rbp
0x00000001092c3774: data32 xchg %ax,%ax
0x00000001092c3777: callq 0x0000000109298f20 ; OopMap{off=188}
;*getstatic out
; - test.TestClient::[email protected] (line 13)
; {runtime_call}
0x00000001092c377c: callq 0x0000000108c1c202 ;*getstatic out
; - test.TestClient::[email protected] (line 13)
; {runtime_call}
Также у вас должен быть файл hotspot.log с шагами компилятора JIT. Вот выдержка:
<phase name='optimizer' nodes='114' live='77' stamp='0.100'>
<phase name='idealLoop' nodes='115' live='67' stamp='0.100'>
<loop_tree>
<loop idx='119' >
<loop idx='185' main_loop='185' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='197' live='111' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='197' live='111' stamp='0.101'>
<loop_tree>
<loop idx='202' >
<loop idx='159' inner_loop='1' pre_loop='131' >
</loop>
<loop idx='210' inner_loop='1' main_loop='210' >
</loop>
<loop idx='138' inner_loop='1' post_loop='131' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='221' live='113' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='221' live='113' stamp='0.101'>
<loop_tree>
<loop idx='202' >
<loop idx='159' inner_loop='1' pre_loop='131' >
</loop>
<loop idx='210' inner_loop='1' main_loop='210' >
</loop>
<loop idx='138' inner_loop='1' post_loop='131' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='241' live='63' stamp='0.101'/>
</phase>
<phase name='ccp' nodes='241' live='63' stamp='0.101'>
<phase_done name='ccp' nodes='241' live='63' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='241' live='63' stamp='0.101'>
<loop_tree>
<loop idx='202' inner_loop='1' >
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='253' live='56' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='253' live='56' stamp='0.101'>
<phase_done name='idealLoop' nodes='253' live='33' stamp='0.101'/>
</phase>
<phase_done name='optimizer' nodes='253' live='33' stamp='0.101'/>
</phase>
Вы можете дополнительно проанализировать файл hotspot.log, созданный компилятором JIT, с помощью инструмента JitWatch https://github.com/AdoptOpenJDK/jitwatch/wiki
Чтобы отключить компилятор JIT и запустить виртуальную машину Java во все интерпретируемом режиме, вы можете использовать опцию -Djava.compiler = NONE JVM.
Аналогичный вопрос в этом сообщении Почему моя JVM делает некоторую оптимизацию цикла выполнения и делает мой код ошибкой?