JVM彙編總結
無關性的基石
計算機只認識0和1,所以我們寫的程式需要被編譯器翻譯成0和1才能被計算機執行。10多年的時間過去了,今天的計算機仍然只識別0和1,但由於最近10年內虛擬機器及建立在虛擬機器之上的大量程式語言如後春筍般出現並蓬勃發展,將我們編寫字的程式編譯成二進位制本地機器碼已經不再是唯一的選擇,越來越多的程式語言選擇了與作業系統和機器指令集無關的,平臺中立的格式作為程式編譯後的儲存格式。“一次編寫,到處執行”。
JAVA 虛擬機器規範
ofollow,noindex">https://docs.oracle.com/javase/specs/jvms/se11/html/index.html
java虛擬機器提供的平臺無關性
概念
位元組碼
即JAVA原始檔編譯後的位元組碼檔案,檔案格式內容<<深入理解java 虛擬機器>> 第六章類檔案格式,有詳細講解.包括JVM彙編指令.位元組碼與JVM彙編助記符見<<深入理解JAVA虛擬機器>>附錄B
彙編
JAVA語言的執行時彙編為AT&T彙編,詳見下文
https://www.jianshu.com/p/74d54c9d818dvolatile 關鍵字可見性分析例項
javap 指令可以反JVM彙編
用法: javap <options> <classes> 其中, 可能的選項包括: -help--help-?輸出此用法訊息 -version版本資訊 -v-verbose輸出附加資訊 -l輸出行號和本地變量表 -public僅顯示公共類和成員 -protected顯示受保護的/公共類和成員 -package顯示程式包/受保護的/公共類 和成員 (預設) -p-private顯示所有類和成員 -c對程式碼進行反彙編 -s輸出內部型別簽名 -sysinfo顯示正在處理的類的 系統資訊 (路徑, 大小, 日期, MD5 雜湊) -constants顯示最終常量 -classpath <path>指定查詢使用者類檔案的位置 -cp <path>指定查詢使用者類檔案的位置 -bootclasspath <path>覆蓋引導類檔案的位置
JAVA原始碼
public class VolitaleTest { private static volatile int i = 0; public static void main(String[] args) { i++; } }
檢視JAVAclass檔案位元組碼,注意,這裡是JVM彙編指令,並非執行時彙編
Classfile /D:/sparrow/sparrow-shell/sparrow-test/target/test-classes/com/sparrow/jdk/volatilekey/VolitaleTest.class Last modified 2018-10-4; size 527 bytes MD5 checksum 51ad6d8677911aedc21bf4e1a5ea7343 Compiled from "VolitaleTest.java" public class com.sparrow.jdk.volatilekey.VolitaleTest minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref#4.#21// java/lang/Object."<init>":()V #2 = Fieldref#3.#22// com/sparrow/jdk/volatilekey/VolitaleTest.i:I #3 = Class#23// com/sparrow/jdk/volatilekey/VolitaleTest #4 = Class#24// java/lang/Object #5 = Utf8i #6 = Utf8I #7 = Utf8<init> #8 = Utf8()V #9 = Utf8Code #10 = Utf8LineNumberTable #11 = Utf8LocalVariableTable #12 = Utf8this #13 = Utf8Lcom/sparrow/jdk/volatilekey/VolitaleTest; #14 = Utf8main #15 = Utf8([Ljava/lang/String;)V #16 = Utf8args #17 = Utf8[Ljava/lang/String; #18 = Utf8<clinit> #19 = Utf8SourceFile #20 = Utf8VolitaleTest.java #21 = NameAndType#7:#8// "<init>":()V #22 = NameAndType#5:#6// i:I #23 = Utf8com/sparrow/jdk/volatilekey/VolitaleTest #24 = Utf8java/lang/Object { public com.sparrow.jdk.volatilekey.VolitaleTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1// Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 15: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/sparrow/jdk/volatilekey/VolitaleTest; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic#2// Field i:I 3: iconst_1 4: iadd 5: putstatic#2// Field i:I 8: return LineNumberTable: line 18: 0 line 19: 8 LocalVariableTable: StartLengthSlotNameSignature 090args[Ljava/lang/String; static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: iconst_0 1: putstatic#2// Field i:I 4: return LineNumberTable: line 16: 0 } SourceFile: "VolitaleTest.java"
以上內容與CLASS檔案描述格式一致.
如何驗證VOLITILE 可見性保證
通過以上指令是無法驗證的,需要檢視執行時彙編指令.
java命令
* 虛擬機器引數: * -XX:+PrintAssembly:輸出反彙編內容; * -Xcomp:是讓虛擬機器以編譯模式執行程式碼; * -XX:CompileCommand=dontinline,*ClassName.methodName:讓編譯器不要內聯methodNmae(); * -XX:CompileCommand=compileonly,*ClassName.methodNmae:只編譯methodNmae(); *
命令示例
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*ClassName.methodName ClassFullPath
實際指令碼
java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolitaleTest.main com.sparrow.jdk.volatilekey.VolitaleTest
部分執行時彙編
# {method} {0x0000000019ca0290} 'main' '([Ljava/lang/String;)V' in 'com/sparrow/jdk/volatilekey/VolitaleTest' # parm0:rdx:rdx= '[Ljava/lang/String;' #[sp+0x40](sp of caller) 0x0000000005482360: movdword ptr [rsp+0ffffffffffffa000h],eax 0x0000000005482367: pushrbp 0x0000000005482368: subrsp,30h 0x000000000548236c: movrsi,0d6258530h;{oop(a 'java/lang/Class' = 'com/sparrow/jdk/volatilekey/VolitaleTest')} 0x0000000005482376: movedi,dword ptr [rsi+68h];*getstatic i ; - com.sparrow.jdk.volatilekey.VolitaleTest::main@0 (line 19) 0x0000000005482379: incedi 0x000000000548237b: movdword ptr [rsi+68h],edi 0x000000000548237e: lock add dword ptr [rsp],0h;*putstatic i ; - com.sparrow.jdk.volatilekey.VolitaleTest::main@5 (line 19)
0x000000000548237e: lock add dword ptr [rsp],0h;*putstatic i
查intel 文件lock字首含義,可知其保證可見性
JAVA併發程式設計藝術一書中,對該節有詳細描述.
本文主要介紹一些彙編概念和檢視彙編的實操方法,關於volitile的可見性及如何保證原子性,可參考其他文章。
參考:
《深入理解JAVA虛擬機器》周志明 著