無(wú)關(guān)性的基石
計(jì)算機(jī)只認(rèn)識(shí)0和1官还,所以我們寫的程序需要被編譯器翻譯成0和1才能被計(jì)算機(jī)執(zhí)行屯伞。10多年的時(shí)間過(guò)去了,今天的計(jì)算機(jī)仍然只識(shí)別0和1,但由于最近10年內(nèi)虛擬機(jī)及建立在虛擬機(jī)之上的大量程序語(yǔ)言如后春筍般出現(xiàn)并蓬勃發(fā)展滑潘,將我們編寫字的程序編譯成二進(jìn)制本地機(jī)器碼已經(jīng)不再是唯一的選擇,越來(lái)越多的程序語(yǔ)言選擇了與操作系統(tǒng)和機(jī)器指令集無(wú)關(guān)的钮孵,平臺(tái)中立的格式作為程序編譯后的存儲(chǔ)格式荧库。“一次編寫,到處運(yùn)行”竿裂。
JAVA 虛擬機(jī)規(guī)范
https://docs.oracle.com/javase/specs/jvms/se11/html/index.html
JAVA 語(yǔ)言規(guī)范
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
概念
字節(jié)碼
即JAVA源文件編譯后的字節(jié)碼文件影斑,文件格式內(nèi)容<<深入理解java 虛擬機(jī)>> 第六章類文件格式,有詳細(xì)講解.包括JVM匯編指令.字節(jié)碼與JVM匯編助記符見<<深入理解JAVA虛擬機(jī)>>附錄B
匯編
JAVA語(yǔ)言的運(yùn)行時(shí)匯編為AT&T匯編,詳見下文
http://www.reibang.com/p/74d54c9d818d
volatile 關(guān)鍵字可見性分析實(shí)例
javap 指令可以反JVM匯編
用法: javap <options> <classes>
其中, 可能的選項(xiàng)包括:
-help --help -? 輸出此用法消息
-version 版本信息
-v -verbose 輸出附加信息
-l 輸出行號(hào)和本地變量表
-public 僅顯示公共類和成員
-protected 顯示受保護(hù)的/公共類和成員
-package 顯示程序包/受保護(hù)的/公共類
和成員 (默認(rèn))
-p -private 顯示所有類和成員
-c 對(duì)代碼進(jìn)行反匯編
-s 輸出內(nèi)部類型簽名
-sysinfo 顯示正在處理的類的
系統(tǒng)信息 (路徑, 大小, 日期, MD5 散列)
-constants 顯示最終常量
-classpath <path> 指定查找用戶類文件的位置
-cp <path> 指定查找用戶類文件的位置
-bootclasspath <path> 覆蓋引導(dǎo)類文件的位置
JAVA源代碼
public class VolitaleTest {
private static volatile int i = 0;
public static void main(String[] args) {
i++;
}
}
查看JAVA class文件字節(jié)碼,注意,這里是JVM匯編指令,并非運(yùn)行時(shí)匯編
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 = Utf8 i
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/sparrow/jdk/volatilekey/VolitaleTest;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 <clinit>
#19 = Utf8 SourceFile
#20 = Utf8 VolitaleTest.java
#21 = NameAndType #7:#8 // "<init>":()V
#22 = NameAndType #5:#6 // i:I
#23 = Utf8 com/sparrow/jdk/volatilekey/VolitaleTest
#24 = Utf8 java/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:
Start Length Slot Name Signature
0 5 0 this Lcom/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:
Start Length Slot Name Signature
0 9 0 args [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"
以上內(nèi)容與CLASS文件描述格式一致.
如何驗(yàn)證VOLITILE 可見性保證
通過(guò)以上指令是無(wú)法驗(yàn)證的,需要查看運(yùn)行時(shí)匯編指令.
java命令
* 虛擬機(jī)參數(shù):
* -XX:+PrintAssembly:輸出反匯編內(nèi)容冯痢;
* -Xcomp:是讓虛擬機(jī)以編譯模式執(zhí)行代碼椎组;
* -XX:CompileCommand=dontinline,*ClassName.methodName:讓編譯器不要內(nèi)聯(lián)methodNmae()庇勃;
* -XX:CompileCommand=compileonly,*ClassName.methodNmae:只編譯methodNmae()责嚷;
*
命令示例
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*ClassName.methodName ClassFullPath
實(shí)際腳本
java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolitaleTest.main com.sparrow.jdk.volatilekey.VolitaleTest
部分運(yùn)行時(shí)匯編
# {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: mov dword ptr [rsp+0ffffffffffffa000h],eax
0x0000000005482367: push rbp
0x0000000005482368: sub rsp,30h
0x000000000548236c: mov rsi,0d6258530h ; {oop(a 'java/lang/Class' = 'com/sparrow/jdk/volatilekey/VolitaleTest')}
0x0000000005482376: mov edi,dword ptr [rsi+68h] ;*getstatic i
; - com.sparrow.jdk.volatilekey.VolitaleTest::main@0 (line 19)
0x0000000005482379: inc edi
0x000000000548237b: mov dword 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并發(fā)編程藝術(shù)一書中,對(duì)該節(jié)有詳細(xì)描述.
本文主要介紹一些匯編概念和查看匯編的實(shí)操方法再层,關(guān)于volitile的可見性及如何保證原子性贸铜,可參考其他文章。
參考:
《深入理解JAVA虛擬機(jī)》周志明 著