JIT最適化による問題
背景
最近1段のコードに出会って、注意深く下の異常な原因を分析して、過程の中で多くの問題に出会って、ここでまとめますpublic class NoVisiabilityTest {
private static class ReadThread extends Thread {
private boolean ready;
private int number;
public void run() {
while (!ready) {
number++;
//System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
}
public static void main(String[] args) throws InterruptedException {
ReadThread readThread = new ReadThread();
readThread.start();
Thread.sleep(200);
readThread.readyOn();
System.out.println(readThread.ready);
}
}
コードの出典:http://cache.baiducontent.com/c?m=9d78d513d99f1ceb03accf2d1a17a736420adb2577c0d1653983d20e87231b1f483ca5fd65351177ced8273356eb1e4bea87672f681e78e4df9b9f4aabe8c37f38885133671cf0410f&p=8162c54ad5c042f50be296234e55cd&newp=c379d25483904ead08e2977d0e0083231615d70e3cd0da1f&user=baidu&fm=sc&query=NoVisiabilitytest&qid=9e9aba67000027e7&p1=1
まず、これは問題のあるデッドループコードであり、文の解釈を特に理解していないため、自分で実践した.
問題解決
デッドサイクルの問題を解決するにはvalotileでready変数を命名するか、while (!ready) {
number++;
System.out.println("OK");
}
関連原理は上記の出典を参考にしたり、フォローしたりすることができます.一人一人の観点が違うので、私も自分の見解が正しいことを確保しません.
問題の追跡
コードの簡略化public class Thread1{
private boolean ready;
private int number;
public void run0() {
while (!ready) {
number++;
//System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
public static void main(String[] args) {
Thread1 inst = new Thread1();
//System.out.println("get ready");
inst.run0();
}
}
コンパイル期間
javap -c Thread1 Compiled from "Thread1.java"
public class Thread1 {
public Thread1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void run0();
Code:
0: aload_0
1: getfield #2 // Field ready:Z
4: ifne 20
7: aload_0
8: dup
9: getfield #3 // Field number:I
12: iconst_1
13: iadd
14: putfield #3 // Field number:I
17: goto 0
20: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_0
24: getfield #2 // Field ready:Z
27: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
30: return
public void readyOn();
Code:
0: aload_0
1: iconst_1
2: putfield #2 // Field ready:Z
5: return
public static void main(java.lang.String[]);
Code:
0: new #6 // class Thread1
3: dup
4: invokespecial #7 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #8 // Method run0:()V
12: return
}
実行時
実行時の最適化は位置決めが困難で、hsdisプラグインをインストールする必要があり、環境構築は参照できる.http://www.infoq.com/cn/articles/zzm-java-hsdis-jvm/
CMD: java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*Thread1.run0 -XX:CompileCommand=compileonly,*Thread1.run0 Thread1 > T1.log
ASM: Decoding compiled method 0x0000000110ac10d0:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Constants]
# {method} 'run0' '()V' in 'Thread1'
# [sp+0x20] (sp of caller)
0x0000000110ac1200: mov 0x8(%rsi),%r10d
0x0000000110ac1204: shl $0x3,%r10
0x0000000110ac1208: cmp %r10,%rax
0x0000000110ac120b: jne 0x0000000110a96960 ; {runtime_call}
0x0000000110ac1211: data32 xchg %ax,%ax
0x0000000110ac1214: nopl 0x0(%rax,%rax,1)
0x0000000110ac121c: data32 data32 xchg %ax,%ax
[Verified Entry Point]
0x0000000110ac1220: mov %eax,-0x14000(%rsp)
0x0000000110ac1227: push %rbp
0x0000000110ac1228: sub $0x10,%rsp ;*synchronization entry
; - Thread2::run0@-1 (line 8)
0x0000000110ac122c: mov %rsi,%r10
0x0000000110ac122f: movzbl 0x10(%rsi),%r11d
0x0000000110ac1234: test %r11d,%r11d
0x0000000110ac1237: jne 0x0000000110ac124c ;*ifne
; - Thread2::run0@4 (line 8)
0x0000000110ac1239: mov 0xc(%rsi),%r8d ;*getfield number
; - Thread2::run0@9 (line 9)
0x0000000110ac123d: inc %r8d ;*iadd
; - Thread2::run0@13 (line 9)
0x0000000110ac1240: mov %r8d,0xc(%r10) ; OopMap{r10=Oop off=68}
;*goto
; - Thread2::run0@17 (line 9)
0x0000000110ac1244: test %eax,-0x133124a(%rip) # 0x000000010f790000
;*goto
; - Thread2::run0@17 (line 9)
; {poll}
0x0000000110ac124a: jmp 0x0000000110ac123d
0x0000000110ac124c: mov $0x1c,%esi
0x0000000110ac1251: mov %r10,%rbp
0x0000000110ac1254: data32 xchg %ax,%ax
0x0000000110ac1257: callq 0x0000000110a97f20 ; OopMap{rbp=Oop off=92}
;*getstatic out
; - Thread2::run0@20 (line 11)
; {runtime_call}
0x0000000110ac125c: callq 0x000000011041bb72 ;*getfield number
; - Thread2::run0@9 (line 9)
; {runtime_call}
0x0000000110ac1261: callq 0x000000011041bb72 ; {runtime_call}
0 x 0000000110 ac 123 dから0 x 0000000110 ac 124 aまでは、デッドサイクル(0 x 0000000110 ac 1244行ポーリングsafepoint)に陥っていることがわかりますが、翻訳されたjavaコードは、次のようになります.while (!ready) {
if(ready) {
System.out.println("OK");
} else {
while(true) {
number++;
}
}
}
uncommon_trap()が引き起こす思考
コードは次のとおりです.public class Thread2{
private boolean ready;
private int number;
public void run0() {
while (!ready) {
number++;
System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
public static void main(String[] args) {
Thread1 inst = new Thread1();
//System.out.println("get ready");
inst.run0();
}
}
JIT逆コンパイル後のアセンブリは以下の通りである.[Verified Entry Point]
0x00000001104c1220: mov %eax,-0x14000(%rsp)
0x00000001104c1227: push %rbp
0x00000001104c1228: sub $0x10,%rsp ;*synchronization entry
; - Thread1::run0@-1 (line 8)
0x00000001104c122c: movzbl 0x10(%rsi),%r10d
0x00000001104c1231: test %r10d,%r10d
0x00000001104c1234: je 0x00000001104c1249 ;*ifne
; - Thread1::run0@4 (line 8)
0x00000001104c1236: mov %rsi,%rbp
0x00000001104c1239: mov $0x1e,%esi
0x00000001104c123e: nop
0x00000001104c123f: callq 0x0000000110497f20 ; OopMap{rbp=Oop off=68}
;*getstatic out
; - Thread1::run0@28 (line 12)
; {runtime_call}
0x00000001104c1244: callq 0x000000010fe1bb72 ;*getstatic out
; - Thread1::run0@28 (line 12)
; {runtime_call}
0x00000001104c1249: incl 0xc(%rsi) ;*synchronization entry
; - Thread1::run0@-1 (line 8)
0x00000001104c124c: mov %rsi,%rbp
0x00000001104c124f: mov $0x1e,%esi
0x00000001104c1254: data32 xchg %ax,%ax
0x00000001104c1257: callq 0x0000000110497f20 ; OopMap{rbp=Oop off=92}
;*getstatic out
; - Thread1::run0@17 (line 10)
; {runtime_call}
0x00000001104c125c: callq 0x000000010fe1bb72 ;*getstatic out
; - Thread1::run0@17 (line 10)
; {runtime_call}
......
OK
OK
OK
......
Decoding compiled method 0x00000001104bf3d0:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} 'run0' '()V' in 'Thread1'
0x00000001104bf540: callq 0x000000010fe1bb72 ; {runtime_call}
0x00000001104bf545: data32 data32 nopw 0x0(%rax,%rax,1)
0x00000001104bf550: mov %eax,-0x14000(%rsp)
0x00000001104bf557: push %rbp
0x00000001104bf558: sub $0x10,%rsp
0x00000001104bf55c: mov (%rsi),%rbp
0x00000001104bf55f: mov %rsi,%rdi
0x00000001104bf562: movabs $0x10fe75a70,%r10
0x00000001104bf56c: callq *%r10
0x00000001104bf56f: mov 0x8(%rbp),%r11d ; implicit exception: dispatches to 0x00000001104bf601
0x00000001104bf573: cmp $0xef610642,%r11d ; {oop('Thread1')}
0x00000001104bf57a: jne 0x00000001104bf5dc
0x00000001104bf57c: jmp 0x00000001104bf594
0x00000001104bf57e: xchg %ax,%ax ;*aload_0
; - Thread1::run0@0 (line 8)
0x00000001104bf580: lea (%r12,%r10,8),%rsi ;*getstatic out
; - Thread1::run0@17 (line 10)
0x00000001104bf584: movabs $0x7d5654668,%rdx ; {oop("OK")}
0x00000001104bf58e: nop
..................
上のHotspot logでは2つの問題が見られます.
1、この方法は12000+回実行されると二次コンパイルされる
2、初めてコンパイルしたコードにjmpがないなんて、ループがどうやって実現したのか見えない
サガに聞いてみると、その理由は以下の通りです.
私は-Xcompを使って、初めてThread 2を呼び出しました.run 0()の前にPrintStreamクラスはロードされていません.最初にコンパイルしたのはwhileサイクル内とサイクル後にuncommonが1つずつあることですtrap()の呼び出し.するとループはなくなり、ループの有無にかかわらず解釈器に戻って走ります.十分な暑さに走ると2回目のコンパイルがトリガーされます.
ブログを参照してください.http://rednaxelafx.iteye.com/blog/1038324
Q&A
1、JVMは実行を説明しているのではないでしょうか.どうしてアセンブリコードに巻き込まれたのですか.
メソッドが実行される回数+メソッド内の総ループの実行回数>しきい値は、JITがローカルコードにコンパイルされることをトリガーします.sleep(200)を取り除くとデッドサイクルはありませんが、時間が経つにつれてデッドサイクルが現れることは必至です
2、valotileネーミング変数はこの問題を解決することができますが、一般的なコンカレントプログラムではこのように設計されています.原理は何ですか.本当に徹底的に解決できますか?
実はアセンブリコードからvalotileの原理を推敲することができて、あなたはセグメントコードを書いてJITをコンパイルしてみて参考にすることができます.
私がもっと賛成している観点は、volatileキーワードはプロセッサの最適化を防止するので、変数に対してレジスタに入れたり、最適化されたりしません.ただしvolatileではCacheからCPUがデータを読み取るのを防止することはできません.ブログを参照してください.http://blog.csdn.net/hengyunabc/article/details/26822801
public class NoVisiabilityTest {
private static class ReadThread extends Thread {
private boolean ready;
private int number;
public void run() {
while (!ready) {
number++;
//System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
}
public static void main(String[] args) throws InterruptedException {
ReadThread readThread = new ReadThread();
readThread.start();
Thread.sleep(200);
readThread.readyOn();
System.out.println(readThread.ready);
}
}
デッドサイクルの問題を解決するにはvalotileでready変数を命名するか、
while (!ready) {
number++;
System.out.println("OK");
}
関連原理は上記の出典を参考にしたり、フォローしたりすることができます.一人一人の観点が違うので、私も自分の見解が正しいことを確保しません.
問題の追跡
コードの簡略化public class Thread1{
private boolean ready;
private int number;
public void run0() {
while (!ready) {
number++;
//System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
public static void main(String[] args) {
Thread1 inst = new Thread1();
//System.out.println("get ready");
inst.run0();
}
}
コンパイル期間
javap -c Thread1 Compiled from "Thread1.java"
public class Thread1 {
public Thread1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void run0();
Code:
0: aload_0
1: getfield #2 // Field ready:Z
4: ifne 20
7: aload_0
8: dup
9: getfield #3 // Field number:I
12: iconst_1
13: iadd
14: putfield #3 // Field number:I
17: goto 0
20: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_0
24: getfield #2 // Field ready:Z
27: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
30: return
public void readyOn();
Code:
0: aload_0
1: iconst_1
2: putfield #2 // Field ready:Z
5: return
public static void main(java.lang.String[]);
Code:
0: new #6 // class Thread1
3: dup
4: invokespecial #7 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #8 // Method run0:()V
12: return
}
実行時
実行時の最適化は位置決めが困難で、hsdisプラグインをインストールする必要があり、環境構築は参照できる.http://www.infoq.com/cn/articles/zzm-java-hsdis-jvm/
CMD: java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*Thread1.run0 -XX:CompileCommand=compileonly,*Thread1.run0 Thread1 > T1.log
ASM: Decoding compiled method 0x0000000110ac10d0:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Constants]
# {method} 'run0' '()V' in 'Thread1'
# [sp+0x20] (sp of caller)
0x0000000110ac1200: mov 0x8(%rsi),%r10d
0x0000000110ac1204: shl $0x3,%r10
0x0000000110ac1208: cmp %r10,%rax
0x0000000110ac120b: jne 0x0000000110a96960 ; {runtime_call}
0x0000000110ac1211: data32 xchg %ax,%ax
0x0000000110ac1214: nopl 0x0(%rax,%rax,1)
0x0000000110ac121c: data32 data32 xchg %ax,%ax
[Verified Entry Point]
0x0000000110ac1220: mov %eax,-0x14000(%rsp)
0x0000000110ac1227: push %rbp
0x0000000110ac1228: sub $0x10,%rsp ;*synchronization entry
; - Thread2::run0@-1 (line 8)
0x0000000110ac122c: mov %rsi,%r10
0x0000000110ac122f: movzbl 0x10(%rsi),%r11d
0x0000000110ac1234: test %r11d,%r11d
0x0000000110ac1237: jne 0x0000000110ac124c ;*ifne
; - Thread2::run0@4 (line 8)
0x0000000110ac1239: mov 0xc(%rsi),%r8d ;*getfield number
; - Thread2::run0@9 (line 9)
0x0000000110ac123d: inc %r8d ;*iadd
; - Thread2::run0@13 (line 9)
0x0000000110ac1240: mov %r8d,0xc(%r10) ; OopMap{r10=Oop off=68}
;*goto
; - Thread2::run0@17 (line 9)
0x0000000110ac1244: test %eax,-0x133124a(%rip) # 0x000000010f790000
;*goto
; - Thread2::run0@17 (line 9)
; {poll}
0x0000000110ac124a: jmp 0x0000000110ac123d
0x0000000110ac124c: mov $0x1c,%esi
0x0000000110ac1251: mov %r10,%rbp
0x0000000110ac1254: data32 xchg %ax,%ax
0x0000000110ac1257: callq 0x0000000110a97f20 ; OopMap{rbp=Oop off=92}
;*getstatic out
; - Thread2::run0@20 (line 11)
; {runtime_call}
0x0000000110ac125c: callq 0x000000011041bb72 ;*getfield number
; - Thread2::run0@9 (line 9)
; {runtime_call}
0x0000000110ac1261: callq 0x000000011041bb72 ; {runtime_call}
0 x 0000000110 ac 123 dから0 x 0000000110 ac 124 aまでは、デッドサイクル(0 x 0000000110 ac 1244行ポーリングsafepoint)に陥っていることがわかりますが、翻訳されたjavaコードは、次のようになります.while (!ready) {
if(ready) {
System.out.println("OK");
} else {
while(true) {
number++;
}
}
}
uncommon_trap()が引き起こす思考
コードは次のとおりです.public class Thread2{
private boolean ready;
private int number;
public void run0() {
while (!ready) {
number++;
System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
public static void main(String[] args) {
Thread1 inst = new Thread1();
//System.out.println("get ready");
inst.run0();
}
}
JIT逆コンパイル後のアセンブリは以下の通りである.[Verified Entry Point]
0x00000001104c1220: mov %eax,-0x14000(%rsp)
0x00000001104c1227: push %rbp
0x00000001104c1228: sub $0x10,%rsp ;*synchronization entry
; - Thread1::run0@-1 (line 8)
0x00000001104c122c: movzbl 0x10(%rsi),%r10d
0x00000001104c1231: test %r10d,%r10d
0x00000001104c1234: je 0x00000001104c1249 ;*ifne
; - Thread1::run0@4 (line 8)
0x00000001104c1236: mov %rsi,%rbp
0x00000001104c1239: mov $0x1e,%esi
0x00000001104c123e: nop
0x00000001104c123f: callq 0x0000000110497f20 ; OopMap{rbp=Oop off=68}
;*getstatic out
; - Thread1::run0@28 (line 12)
; {runtime_call}
0x00000001104c1244: callq 0x000000010fe1bb72 ;*getstatic out
; - Thread1::run0@28 (line 12)
; {runtime_call}
0x00000001104c1249: incl 0xc(%rsi) ;*synchronization entry
; - Thread1::run0@-1 (line 8)
0x00000001104c124c: mov %rsi,%rbp
0x00000001104c124f: mov $0x1e,%esi
0x00000001104c1254: data32 xchg %ax,%ax
0x00000001104c1257: callq 0x0000000110497f20 ; OopMap{rbp=Oop off=92}
;*getstatic out
; - Thread1::run0@17 (line 10)
; {runtime_call}
0x00000001104c125c: callq 0x000000010fe1bb72 ;*getstatic out
; - Thread1::run0@17 (line 10)
; {runtime_call}
......
OK
OK
OK
......
Decoding compiled method 0x00000001104bf3d0:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} 'run0' '()V' in 'Thread1'
0x00000001104bf540: callq 0x000000010fe1bb72 ; {runtime_call}
0x00000001104bf545: data32 data32 nopw 0x0(%rax,%rax,1)
0x00000001104bf550: mov %eax,-0x14000(%rsp)
0x00000001104bf557: push %rbp
0x00000001104bf558: sub $0x10,%rsp
0x00000001104bf55c: mov (%rsi),%rbp
0x00000001104bf55f: mov %rsi,%rdi
0x00000001104bf562: movabs $0x10fe75a70,%r10
0x00000001104bf56c: callq *%r10
0x00000001104bf56f: mov 0x8(%rbp),%r11d ; implicit exception: dispatches to 0x00000001104bf601
0x00000001104bf573: cmp $0xef610642,%r11d ; {oop('Thread1')}
0x00000001104bf57a: jne 0x00000001104bf5dc
0x00000001104bf57c: jmp 0x00000001104bf594
0x00000001104bf57e: xchg %ax,%ax ;*aload_0
; - Thread1::run0@0 (line 8)
0x00000001104bf580: lea (%r12,%r10,8),%rsi ;*getstatic out
; - Thread1::run0@17 (line 10)
0x00000001104bf584: movabs $0x7d5654668,%rdx ; {oop("OK")}
0x00000001104bf58e: nop
..................
上のHotspot logでは2つの問題が見られます.
1、この方法は12000+回実行されると二次コンパイルされる
2、初めてコンパイルしたコードにjmpがないなんて、ループがどうやって実現したのか見えない
サガに聞いてみると、その理由は以下の通りです.
私は-Xcompを使って、初めてThread 2を呼び出しました.run 0()の前にPrintStreamクラスはロードされていません.最初にコンパイルしたのはwhileサイクル内とサイクル後にuncommonが1つずつあることですtrap()の呼び出し.するとループはなくなり、ループの有無にかかわらず解釈器に戻って走ります.十分な暑さに走ると2回目のコンパイルがトリガーされます.
ブログを参照してください.http://rednaxelafx.iteye.com/blog/1038324
Q&A
1、JVMは実行を説明しているのではないでしょうか.どうしてアセンブリコードに巻き込まれたのですか.
メソッドが実行される回数+メソッド内の総ループの実行回数>しきい値は、JITがローカルコードにコンパイルされることをトリガーします.sleep(200)を取り除くとデッドサイクルはありませんが、時間が経つにつれてデッドサイクルが現れることは必至です
2、valotileネーミング変数はこの問題を解決することができますが、一般的なコンカレントプログラムではこのように設計されています.原理は何ですか.本当に徹底的に解決できますか?
実はアセンブリコードからvalotileの原理を推敲することができて、あなたはセグメントコードを書いてJITをコンパイルしてみて参考にすることができます.
私がもっと賛成している観点は、volatileキーワードはプロセッサの最適化を防止するので、変数に対してレジスタに入れたり、最適化されたりしません.ただしvolatileではCacheからCPUがデータを読み取るのを防止することはできません.ブログを参照してください.http://blog.csdn.net/hengyunabc/article/details/26822801
public class Thread1{
private boolean ready;
private int number;
public void run0() {
while (!ready) {
number++;
//System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
public static void main(String[] args) {
Thread1 inst = new Thread1();
//System.out.println("get ready");
inst.run0();
}
}
Compiled from "Thread1.java"
public class Thread1 {
public Thread1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void run0();
Code:
0: aload_0
1: getfield #2 // Field ready:Z
4: ifne 20
7: aload_0
8: dup
9: getfield #3 // Field number:I
12: iconst_1
13: iadd
14: putfield #3 // Field number:I
17: goto 0
20: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_0
24: getfield #2 // Field ready:Z
27: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
30: return
public void readyOn();
Code:
0: aload_0
1: iconst_1
2: putfield #2 // Field ready:Z
5: return
public static void main(java.lang.String[]);
Code:
0: new #6 // class Thread1
3: dup
4: invokespecial #7 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #8 // Method run0:()V
12: return
}
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*Thread1.run0 -XX:CompileCommand=compileonly,*Thread1.run0 Thread1 > T1.log
Decoding compiled method 0x0000000110ac10d0:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Constants]
# {method} 'run0' '()V' in 'Thread1'
# [sp+0x20] (sp of caller)
0x0000000110ac1200: mov 0x8(%rsi),%r10d
0x0000000110ac1204: shl $0x3,%r10
0x0000000110ac1208: cmp %r10,%rax
0x0000000110ac120b: jne 0x0000000110a96960 ; {runtime_call}
0x0000000110ac1211: data32 xchg %ax,%ax
0x0000000110ac1214: nopl 0x0(%rax,%rax,1)
0x0000000110ac121c: data32 data32 xchg %ax,%ax
[Verified Entry Point]
0x0000000110ac1220: mov %eax,-0x14000(%rsp)
0x0000000110ac1227: push %rbp
0x0000000110ac1228: sub $0x10,%rsp ;*synchronization entry
; - Thread2::run0@-1 (line 8)
0x0000000110ac122c: mov %rsi,%r10
0x0000000110ac122f: movzbl 0x10(%rsi),%r11d
0x0000000110ac1234: test %r11d,%r11d
0x0000000110ac1237: jne 0x0000000110ac124c ;*ifne
; - Thread2::run0@4 (line 8)
0x0000000110ac1239: mov 0xc(%rsi),%r8d ;*getfield number
; - Thread2::run0@9 (line 9)
0x0000000110ac123d: inc %r8d ;*iadd
; - Thread2::run0@13 (line 9)
0x0000000110ac1240: mov %r8d,0xc(%r10) ; OopMap{r10=Oop off=68}
;*goto
; - Thread2::run0@17 (line 9)
0x0000000110ac1244: test %eax,-0x133124a(%rip) # 0x000000010f790000
;*goto
; - Thread2::run0@17 (line 9)
; {poll}
0x0000000110ac124a: jmp 0x0000000110ac123d
0x0000000110ac124c: mov $0x1c,%esi
0x0000000110ac1251: mov %r10,%rbp
0x0000000110ac1254: data32 xchg %ax,%ax
0x0000000110ac1257: callq 0x0000000110a97f20 ; OopMap{rbp=Oop off=92}
;*getstatic out
; - Thread2::run0@20 (line 11)
; {runtime_call}
0x0000000110ac125c: callq 0x000000011041bb72 ;*getfield number
; - Thread2::run0@9 (line 9)
; {runtime_call}
0x0000000110ac1261: callq 0x000000011041bb72 ; {runtime_call}
while (!ready) {
if(ready) {
System.out.println("OK");
} else {
while(true) {
number++;
}
}
}
コードは次のとおりです.
public class Thread2{
private boolean ready;
private int number;
public void run0() {
while (!ready) {
number++;
System.out.println("OK");
}
System.out.println(ready);
}
public void readyOn() {
this.ready = true;
}
public static void main(String[] args) {
Thread1 inst = new Thread1();
//System.out.println("get ready");
inst.run0();
}
}
JIT逆コンパイル後のアセンブリは以下の通りである.
[Verified Entry Point]
0x00000001104c1220: mov %eax,-0x14000(%rsp)
0x00000001104c1227: push %rbp
0x00000001104c1228: sub $0x10,%rsp ;*synchronization entry
; - Thread1::run0@-1 (line 8)
0x00000001104c122c: movzbl 0x10(%rsi),%r10d
0x00000001104c1231: test %r10d,%r10d
0x00000001104c1234: je 0x00000001104c1249 ;*ifne
; - Thread1::run0@4 (line 8)
0x00000001104c1236: mov %rsi,%rbp
0x00000001104c1239: mov $0x1e,%esi
0x00000001104c123e: nop
0x00000001104c123f: callq 0x0000000110497f20 ; OopMap{rbp=Oop off=68}
;*getstatic out
; - Thread1::run0@28 (line 12)
; {runtime_call}
0x00000001104c1244: callq 0x000000010fe1bb72 ;*getstatic out
; - Thread1::run0@28 (line 12)
; {runtime_call}
0x00000001104c1249: incl 0xc(%rsi) ;*synchronization entry
; - Thread1::run0@-1 (line 8)
0x00000001104c124c: mov %rsi,%rbp
0x00000001104c124f: mov $0x1e,%esi
0x00000001104c1254: data32 xchg %ax,%ax
0x00000001104c1257: callq 0x0000000110497f20 ; OopMap{rbp=Oop off=92}
;*getstatic out
; - Thread1::run0@17 (line 10)
; {runtime_call}
0x00000001104c125c: callq 0x000000010fe1bb72 ;*getstatic out
; - Thread1::run0@17 (line 10)
; {runtime_call}
......
OK
OK
OK
......
Decoding compiled method 0x00000001104bf3d0:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} 'run0' '()V' in 'Thread1'
0x00000001104bf540: callq 0x000000010fe1bb72 ; {runtime_call}
0x00000001104bf545: data32 data32 nopw 0x0(%rax,%rax,1)
0x00000001104bf550: mov %eax,-0x14000(%rsp)
0x00000001104bf557: push %rbp
0x00000001104bf558: sub $0x10,%rsp
0x00000001104bf55c: mov (%rsi),%rbp
0x00000001104bf55f: mov %rsi,%rdi
0x00000001104bf562: movabs $0x10fe75a70,%r10
0x00000001104bf56c: callq *%r10
0x00000001104bf56f: mov 0x8(%rbp),%r11d ; implicit exception: dispatches to 0x00000001104bf601
0x00000001104bf573: cmp $0xef610642,%r11d ; {oop('Thread1')}
0x00000001104bf57a: jne 0x00000001104bf5dc
0x00000001104bf57c: jmp 0x00000001104bf594
0x00000001104bf57e: xchg %ax,%ax ;*aload_0
; - Thread1::run0@0 (line 8)
0x00000001104bf580: lea (%r12,%r10,8),%rsi ;*getstatic out
; - Thread1::run0@17 (line 10)
0x00000001104bf584: movabs $0x7d5654668,%rdx ; {oop("OK")}
0x00000001104bf58e: nop
..................
上のHotspot logでは2つの問題が見られます.
1、この方法は12000+回実行されると二次コンパイルされる
2、初めてコンパイルしたコードにjmpがないなんて、ループがどうやって実現したのか見えない
サガに聞いてみると、その理由は以下の通りです.
私は-Xcompを使って、初めてThread 2を呼び出しました.run 0()の前にPrintStreamクラスはロードされていません.最初にコンパイルしたのはwhileサイクル内とサイクル後にuncommonが1つずつあることですtrap()の呼び出し.するとループはなくなり、ループの有無にかかわらず解釈器に戻って走ります.十分な暑さに走ると2回目のコンパイルがトリガーされます.
ブログを参照してください.http://rednaxelafx.iteye.com/blog/1038324