大部份內容都是《深刻理解Java虛擬機上的內容》的總結,少部份內容是來自於網上或者本身的理解。讀完應該會把沒筆記的markdown文件放在 github上。
本部分筆記對應的是《深刻理解Java虛擬機》最後幾章。git
生成抽象語法樹的階段github
符號表是由一組符號地址和符號信息構成的表格編程
提供一組插入式註解處理的標準API在編譯期間對註解進行處理,咱們能夠把它看作一組編譯器的插件,在這些插件裏面,能夠讀取,修改,添加抽象語法樹中的任意元素。若是這些插件在註解期間對語法書進行了修改,編譯器將會到解析及符號表填充的過程從新處理,知道全部插入式註解處理器都沒有再對語法樹進行修改成止,每一次循環稱爲一個Round。api
public class GenericTypes{ public static void method(List<String> list){ } public static void method(List<Integer> list){ } }
這段代碼不能被編譯,由於類型擦除後都變成原生類型List<E>。數組
public class GenericTypes{ public static int method(List<String> list){ } public static String method(List<Integer> list){ } }
這段代碼是能夠被編譯運行的。方法重載要求方法具有不一樣的特徵簽名,返回值並不包含在方法的特徵簽名之中,因此返回值不參與重載選擇,可是在Class文件格式之中,只要描述符不是徹底一致的兩個方法就能夠共存。安全
HotSpot中有兩個編譯器,分別稱爲Client Compiler和Server Compiler。markdown
編譯器都是以整個方法做爲編譯對象多線程
對於方法調用的熱點數據斷定oop
基於計數器的熱點探測(Count Based Hot Spot Detection),採用這種方法的虛擬機會爲每一個方法創建一個計數器,統計方法的執行次數,若是執行次數超過必定的闕值,就編譯。優化
逃逸分析的基本行爲就是分析對象動態做用域:當一個對象在方法中被定義後,它可能被外部方法所引用,例如做爲調用參數傳遞到其餘方法中稱爲方法逃逸。若是被外部線程訪問,稱爲線程逃逸。
其實屏蔽的是操做系統的多級內存模型
Java內存模型規定了全部的變量都存儲在主內存(Main Memory)中,每一個線程還有本身的工做內存(Working Memory),線程的工做內存保存了被該線程使用到的變量的主內存副本拷貝,線程對變量的全部操做(讀取,賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法訪問對方工做內存中的變量,線程間變量值的傳遞均須要經過主內存來完成。
Java的內存模型中定義了8個操做來完成,虛擬機實現時必需要保證下面說起的每一種操做都是原子的。
基本規則
volatile,依然有工做內存的拷貝,可是因爲它特殊的操做順序性規定,因此看起來若是直接在主內存中讀寫訪問通常。
符合如下兩個規則,則能夠不加鎖
可見性
當一個線程修改了這個變量的值,新值對於其餘線程是能夠當即獲得的。普通變量在線程間傳遞均須要經過主內存來完成。
禁止指令重排
內核線程實現
內核線程(Kernel-Level Thread)就是直接由操做系統內核支持的線程,這種線程由內核來完成線程切換,內核經過操縱調度器對線程進行調度,並負責將線程的任務映射各個處理器上。支持多線程的內核叫作多線程內核
輕量級進程(Light Weight Thread),輕量級進程就是一般意義上所講的線程,因爲每一個輕量級進程都由一個內核線程支持,所以只有先支持內核線程,纔能有輕量級進程。這種輕量級進程與內核之間1:1的關係稱爲一對一的線程模型。
會常常在用戶態和內核臺切換。
用戶線程實現
用戶線程指的是徹底創建用戶空間的線程庫上,系統內核不能感知線程的存在。
用戶線程加輕量級進程實現
內核線程和用戶線程一塊兒使用的方式
協同式線程調度(Cooperative Threads-Scheduling)
線程的執行時間由線程自己控制,線程把本身的工做執行完成以後,要主動通知操做系統切換到另外一個線程
搶佔式線程調度(Preemptive Threads-Scheduling)
每一個線程將由操做系統來分配執行時間,線程切換不有線程自己來決定。Java使用的線程調度方式就是搶佔式調度。Java的線程是經過調用操做系統的api來實現的。因此Java提供的線程優先級也是要依賴於系統。
就緒,運行,等待,阻塞,結束
無論運行時環境如何,調用者都不須要任何額外的同步措施
synchronize的是可重入鎖
ReentrantLock
CAS
共享數據的鎖定狀態只會持續很短的一段時間,爲了這段時間去掛起和恢復線程並不值得。爲了讓線程等待,只須要讓線程執行一個忙循環體,這就是共享自旋鎖。
鎖消除是指在虛擬機即便編譯在運行時,對一些代碼上要求同步,可是檢測到不可能存在共享數據競爭的鎖進行消除。有的時候會編譯優化產生鎖
對於一段代碼,在不一樣代碼塊反覆加鎖,不如直接給這段代碼加鎖。
HotSpot虛擬機的對象頭(Object Header)分爲兩部分信息,第一部分用於存儲對象自身的運行時數據,如HashCode,GC分代年齡(Generational GC age),這部分數據的長度在32位和64位的虛擬機分別爲32bit,64bit,官方稱爲「Mark Word」。另一部分用於存儲指向方法區對象類型數據的指針,若是是數組對象,還會有一個額外的部分存儲數組長度。
加鎖
在代碼進入同步塊的時候,若是此同步對象沒有被鎖定(鎖標誌位爲「01」),虛擬機首先將當前線程的棧幀中創建一個名爲鎖記錄(Lock Record)空間,用於存儲對象目前的Mark Work的拷貝。
虛擬機將使用CAS才作嘗試將對象的Mark Word更新爲指向Lock Record的指針。若是更新成功,那麼這個線程就擁有了該對象的鎖,而且對象Mark Word的鎖標誌編程 「00」,表示此對象處於輕量級鎖定狀態
若是這個更新操做失敗了,虛擬機首先會檢查對象的Mark Word是否指向當前線程的棧幀,若是是,則說明當前線程已經擁有鎖了。若是不是那輕量級線鎖就再也不有效,要膨脹爲重量級鎖。鎖的標誌變爲「10」。
解鎖
經過CAS操做進行,若是對象的Mark Word仍然指向該線程的鎖記錄,那麼就用CAS操做把對象當前的Mark Word和線程中複製的Displaced Mark Word替換回來,若是替換成功,同步過程就完成了。若是失敗,說明有其餘線程嘗試獲取該鎖,那就釋放該鎖的同時,喚醒被掛起的線程。
先用CAS看看能不能修改對象鎖的狀態,若是不能就用掛起和阻塞的方式去修改,以代表該對象是被鎖定的。從輕量級鎖膨脹爲重量級鎖。
當鎖對象第一次被線程獲取的時候,虛擬機將會把對象頭中的標誌位設爲「01」,即爲偏向模式。同時使用CAS操做把獲取到這個鎖的ID記錄在對象的Mark Word之中,若是CAS操做成功,持有偏向鎖的線程之後每一次進入這個鎖相關的同步塊,都不須要進行任何同步操做。
當另一個線程去嘗試獲取這個鎖的時候,偏向模式結束,後續按照輕量級鎖的方式進行處理。
若是隻有一個線程進入臨界區,那麼就是偏向鎖。若是有第二線程想要進入,那麼就會鎖膨脹爲輕量級鎖。