[英]static final fields vs TrustFinalNonStaticFields
假設我有這個簡單的方法:
static final Integer me = Integer.parseInt("2");
static int go() {
return me * 2;
}
對於 javac, me
不是常量(根據 JLS 規則),但對於 JIT 來說很可能是。
我嘗試使用以下方法對此進行測試:
public class StaticFinal {
public static void main(String[] args) {
int hash = 0;
for(int i=0;i<1000_000;++i){
hash = hash ^ go();
}
System.out.println(hash);
}
static final Integer me = Integer.parseInt("2");
static int go() {
return me * 2;
}
}
並運行它:
java -XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
"-XX:CompileCommand=print,StaticFinal.go"
-XX:PrintAssemblyOptions=intel
StaticFinal.java
我不知道組裝很好,但這很明顯:
mov eax,0x4
go
的結果立即為4
,即:JIT “信任” me
是一個常數,因此2 * 2 = 4
。
如果我放棄static
並將代碼更改為:
public class NonStaticFinal {
static NonStaticFinal instance = new NonStaticFinal();
public static void main(String[] args) {
int hash = 0;
for(int i=0;i<1000_000;++i){
hash = hash ^ instance.go();
}
System.out.println(hash);
}
final Integer me = Integer.parseInt("2");
int go() {
return me * 2;
}
}
並運行它:
java -XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
"-XX:CompileCommand=print,NonStaticFinal.go"
-XX:PrintAssemblyOptions=intel
NonStaticFinal.java
我確實在匯編中看到:
shl eax,1
這實際上是me
與2
的乘積,通過移位完成。 所以 JIT 不相信me
是一個常數,這是意料之中的。
現在的問題。 我想如果我添加TrustFinalNonStaticFields
標志,我會看到相同的mov eax 0x4
,即:運行:
java -XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
"-XX:CompileCommand=print,NonStaticFinal.go"
-XX:+UnlockExperimentalVMOptions
-XX:+TrustFinalNonStaticFields
-XX:PrintAssemblyOptions=intel
NonStaticFinal.java
應該顯示mov eax,0x4
,但令我驚訝的是它沒有,並且代碼保持為:
shl eax,1
有人可以解釋發生了什么以及我缺少什么嗎?
TrustFinalNonStaticFields
允許從常量對象折疊final
實例字段。 但是,在您的示例中, instance
字段不是常量,因此折疊me
字段的負載是不正確的,因為instance
object 可能在編譯后的某個時候仍會更改。
此外,您正在打印go
方法的程序集,如果單獨編譯this
方法,則不會將其視為常量。 要查看TrustFinalNonStaticFields
的效果,您需要查看go
方法的內聯版本的程序集,其中接收器是一個常量。 例如:
public class NonStaticFinal {
static final NonStaticFinal instance = new NonStaticFinal();
public static void main(String[] args) {
for (int i = 0; i < 20_000; i++) { // trigger compilation of 'payload'
payload();
}
}
static int payload() {
return instance.go();
}
final Integer me = Integer.parseInt("2");
int go() {
return me * 2;
}
}
運行:
java
-XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
"-XX:CompileCommand=print,NonStaticFinal.payload"
"-XX:CompileCommand=dontinline,NonStaticFinal.payload"
-XX:+UnlockExperimentalVMOptions
-XX:+TrustFinalNonStaticFields
-XX:PrintAssemblyOptions=intel
-Xbatch
NonStaticFinal.java
生成程序集,我們可以看到me
字段的負載 + 乘法在payload
方法中被折疊:
# {method} {0x0000016238c59470} 'payload' '()I' in 'NonStaticFinal'
# [sp+0x20] (sp of caller)
// set up frame
0x00000162283d2500: sub rsp,18h
0x00000162283d2507: mov qword ptr [rsp+10h],rbp ;*synchronization entry
; - NonStaticFinal::payload@-1 (line 12)
// load a constant 4
0x00000162283d250c: mov eax,4h <-------------
// clean up frame
0x00000162283d2511: add rsp,10h
0x00000162283d2515: pop rbp
// safepoint poll
0x00000162283d2516: mov r10,qword ptr [r15+110h]
0x00000162283d251d: test dword ptr [r10],eax ; {poll_return}
// return
0x00000162283d2520: ret
與禁用 TFNSF 的版本相比, me
字段的負載仍然發生:
# {method} {0x00000245f9669470} 'payload' '()I' in 'NonStaticFinal'
# [sp+0x20] (sp of caller)
// stack bang
0x00000245e8d52a00: mov dword ptr [rsp+0ffffffffffff9000h],eax
// set up frame
0x00000245e8d52a07: push rbp
0x00000245e8d52a08: sub rsp,10h ;*synchronization entry
; - NonStaticFinal::payload@-1 (line 12)
// load the 'instance' field. It's a constant, so the address here is constant
0x00000245e8d52a0c: mov r10,70ff107a8h ; {oop(a 'NonStaticFinal'{0x000000070ff107a8})}
// load the (compressed) oop 'me' field at 0ch (first field after the object header)
0x00000245e8d52a16: mov r11d,dword ptr [r10+0ch] ;*getfield me {reexecute=0 rethrow=0 return_oop=0}
; - NonStaticFinal::go@1 (line 18)
; - NonStaticFinal::payload@3 (line 12)
// Load the 'value' field from the Integer object.
// r12 is the heap base, r11 the compressed oop 'Integer', *8 here to uncompress it,
// and again loading the first field after the header at 0ch
0x00000245e8d52a1a: mov eax,dword ptr [r12+r11*8+0ch]; implicit exception: dispatches to 0x00000245e8d52a31
// multiply by 2
// ABI returns ints in the 'eax' register, so no need to shuffle afterwards
0x00000245e8d52a1f: shl eax,1h ;*imul {reexecute=0 rethrow=0 return_oop=0}
; - NonStaticFinal::go@8 (line 18)
; - NonStaticFinal::payload@3 (line 12)
// clean up frame
0x00000245e8d52a21: add rsp,10h
0x00000245e8d52a25: pop rbp
// safepoint poll
0x00000245e8d52a26: mov r10,qword ptr [r15+110h]
0x00000245e8d52a2d: test dword ptr [r10],eax ; {poll_return}
// return
0x00000245e8d52a30: ret
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.