虛擬機(jī)
編譯
-
早期編譯(優(yōu)化)
-
解析與填充符號(hào)表過(guò)程
- 詞法分析-->Token序列
- 語(yǔ)法分析-->抽象語(yǔ)法樹(shù)
- 填充符號(hào)表
注解處理器
-
語(yǔ)義分析與字節(jié)碼生成
-
標(biāo)注檢查
- 變量使用前聲明
- 變量賦值類(lèi)型匹配
- 常量折疊
-
數(shù)據(jù)及控制流分析
- 局部變量使用前賦值
- 方法每條路徑具有返回值
- 所有受查異常被正確處理
解語(yǔ)法糖
-
字節(jié)碼生成
將<<init>()和<<clinit>()方法添加進(jìn)語(yǔ)法樹(shù)
-
-
-
運(yùn)行期(優(yōu)化)
解釋器
-
編譯器
- Client Compiler(C1)
- Server Compoler(C2)
-
分層編譯
- 第0層,程序解釋執(zhí)行,解釋器不開(kāi)啟性能監(jiān)控功能
- 第1層,C1編譯,將字節(jié)碼編譯成本地代碼,進(jìn)行簡(jiǎn)單、可靠的優(yōu)化,如有必要將加入性能監(jiān)控邏輯
- 第2層,C2編譯,將字節(jié)碼編譯成本地代碼,啟用耗時(shí)較長(zhǎng)的優(yōu)化,甚至根據(jù)性能監(jiān)控信息進(jìn)行一些不可靠的激進(jìn)優(yōu)化
編譯觸發(fā):基于計(jì)數(shù)器的熱點(diǎn)探測(cè)
類(lèi)加載
-
加載
“全盤(pán)負(fù)責(zé)委托機(jī)制”:
全盤(pán)負(fù)責(zé),是指當(dāng)一個(gè)ClassLoder裝載一個(gè)類(lèi)時(shí),除非顯示的使用另外一個(gè)ClassLoder,該類(lèi)所依賴(lài)及引用的類(lèi)也由這個(gè)ClassLoder載入;
委托機(jī)制,是指先委托父類(lèi)裝載器尋找目標(biāo)類(lèi),只有在找不到的情況下才從自己的類(lèi)路徑中查找并裝載目標(biāo)類(lèi)。(可防止用戶(hù)修改JRE基礎(chǔ)類(lèi)庫(kù))
-
鏈接
驗(yàn)證
-
準(zhǔn)備
為類(lèi)變量(static 變量)分配內(nèi)存并設(shè)置初始值(零值)
-
解析
將常量池的符號(hào)引用替換為直接引用
-
初始化
執(zhí)行<<clinit>方法(static{}塊和類(lèi)變量賦值動(dòng)作)
-
類(lèi)加載器
-
Bootstrap ClassLoader
- <JAVA_HOME>\lib
-
Extension ClassLoader
- <JAVA_HOME>\lib\ext
-
Application ClassLoader
- 用戶(hù)類(lèi)路徑(ClassPath)
-
-
Tomcat
AppClassLoader load $CATALINA_HOME/bin/bootstrap.jar with CommonClassLoader
-
CommonClassLoader load $CATALINA_HOME/lib
This class loader contains additional classes that are made visible to both Tomcat internal classes and to all web applications.
-
WebApp1\2... load /WEB-INF/classes;/WEB-INF/lib
當(dāng)處理從Web應(yīng)用程序的WebappX類(lèi)加載器加載類(lèi)的請(qǐng)求時(shí),該類(lèi)加載器將首先在本地存儲(chǔ)庫(kù)中查找,而不是在查找之前進(jìn)行委托。有例外。作為JRE基類(lèi)的一部分的類(lèi)不能被覆蓋。
同一個(gè)tomcat中,每個(gè)web app分別有一個(gè)WebAppClassLoader,通過(guò)覆蓋loadclass()方法不做加載委托,實(shí)現(xiàn)各個(gè)web app類(lèi)相互不影響。
-
熱加載
同一個(gè)classLoader不能加載相同名字的class,tomcat實(shí)現(xiàn)熱加載,通過(guò)卸載當(dāng)前web context,重新實(shí)例化一個(gè)WebAppClassLoader,重新加載class。
方法調(diào)用
-
解析調(diào)用
- 編譯期就確定方法,類(lèi)加載階段,將符號(hào)引用轉(zhuǎn)換成直接引用 static,private,父類(lèi)方法
-
分派
-
靜態(tài)分派
- 編譯階段,根據(jù)傳遞參數(shù)的靜態(tài)類(lèi)型調(diào)用方法,方法重載
- 多分派,根據(jù)參數(shù)類(lèi)型和方法接收類(lèi)確定
-
動(dòng)態(tài)分派
- 運(yùn)行期,根據(jù)傳遞參數(shù)的動(dòng)態(tài)類(lèi)型調(diào)用方法,方法重寫(xiě)
- 單分派,在編譯階段確定方法簽名,運(yùn)行期只需確定方法實(shí)際接收類(lèi)
-
-
解析
- 發(fā)生在類(lèi)加載解析階段
-
靜態(tài)分派
- 發(fā)生在編譯階段,由編譯器根據(jù)參數(shù)靜態(tài)類(lèi)型確定調(diào)用方法
Java內(nèi)存結(jié)構(gòu)
-
線(xiàn)程私有
PC(當(dāng)前線(xiàn)程執(zhí)行字節(jié)碼行號(hào)指示)
-
虛擬機(jī)棧
-
棧幀
-
局部變量表(方法參數(shù)、局部定義變量)
- 基本數(shù)據(jù)類(lèi)型
- 引用
操作數(shù)棧
動(dòng)態(tài)連接
方法出口
-
-
-
線(xiàn)程共享
-
堆
-
普通對(duì)象
-
對(duì)象頭
Java程序的對(duì)象頭固定占8字節(jié)(32位系統(tǒng))或12字節(jié)(64位系統(tǒng)默認(rèn)開(kāi)啟壓縮, 不開(kāi)壓縮為16字節(jié))
- Mark Word 鎖狀態(tài)、GC年齡等
- 類(lèi)型指針
- 數(shù)組長(zhǎng)度(如果是數(shù)組)
實(shí)例數(shù)據(jù)(包括父類(lèi)和子類(lèi)字段)
補(bǔ)齊填充(補(bǔ)齊為8位整數(shù)倍)
-
-
-
方法區(qū)
Java7之前存在,由于字符串等被放入運(yùn)行時(shí)常量池,導(dǎo)致一系列OOM問(wèn)題,Java8將其挪到本地內(nèi)存,大小可動(dòng)態(tài)增長(zhǎng)。
-
Class對(duì)象-作為訪問(wèn)方法區(qū)入口(HotSpot)
- 靜態(tài)變量
-
Class字節(jié)碼
魔數(shù)
-
常量池
這里是指Class字節(jié)瑪結(jié)構(gòu)中的一部分。
內(nèi)存中存在運(yùn)行時(shí)常量池,jdk6.0之前存在與方法區(qū)中,jdk7.0存在于堆中,jdk8之后被挪到了本地內(nèi)存中。
- 符號(hào)/直接引用
類(lèi)或接口的訪問(wèn)標(biāo)志(public、abstract等)
類(lèi)、父類(lèi)、接口索引
字段表集合
-
方法表集合
-
Code屬性
-
max_locals
- this+exception params + 本地變量s
max_stack
-
-
-
-
直接內(nèi)存(如NIO的DirectByteBuffer)
GC
safepoint:方法返回前、方法call后、循環(huán)末尾跳轉(zhuǎn)、異常跳轉(zhuǎn)
-
OopMap
- 在safepoint處插入指令,收集GC roots
-
GC Roots
- 寄存器引用、全局變量、棧幀中本地變量(活著的)
遍歷對(duì)象,調(diào)用finalize
-
GC算法
-
Stop The World
-
正在運(yùn)行的普通線(xiàn)程
-
Safe Point
位置:方法返回前、方法call后、循環(huán)末尾跳轉(zhuǎn)、異常跳轉(zhuǎn)
-
實(shí)現(xiàn)
-
OopMap
- 作用:收集GC Roots,將棧內(nèi)的引用偏移,以及寄存器中存放的引用,記錄到OopMap數(shù)據(jù)結(jié)構(gòu)
- 位置:每個(gè)類(lèi)有自己的OopMap,在類(lèi)加載時(shí)生成,記錄類(lèi)中引用的偏移;
-
Stop用戶(hù)線(xiàn)程:輪詢(xún)
-
解釋執(zhí)行
- JVM設(shè)置一個(gè)2字節(jié)的dispatch tables,輪詢(xún)這個(gè)dispatch tables
-
JIT
- test指令:JVM將一個(gè)特定的內(nèi)存頁(yè)置為不可讀,通過(guò)test指令將線(xiàn)程掛起
-
-
-
-
正在運(yùn)行native code的線(xiàn)程
當(dāng)VM thread看到一個(gè)Java線(xiàn)程在執(zhí)行native code,它不需要等待這個(gè)Java線(xiàn)程進(jìn)入阻塞狀態(tài),因?yàn)楫?dāng)Java線(xiàn)程從執(zhí)行native code返回的時(shí)候,Java線(xiàn)程會(huì)去檢查safepoint看是否要block(When returning from the native code, a Java thread must check the safepoint _state to see if we must block)
感覺(jué)和safe region頗相似。
-
阻塞的線(xiàn)程
-
Safe Region
線(xiàn)程阻塞時(shí),標(biāo)識(shí)自己進(jìn)入safe region,此時(shí)該線(xiàn)程引用關(guān)系不會(huì)變化,JVM可安全的標(biāo)記;
線(xiàn)程被喚醒時(shí),先檢查是否已經(jīng)完成根節(jié)點(diǎn)枚舉(或是完成整個(gè)GC,視不同垃圾回收器而定),如果完成,則繼續(xù)執(zhí)行,否則掛起知道收到可以安全離開(kāi)safe region的信號(hào)為止。
-
-
-
遍歷對(duì)象
- GC Roots
- finalize
- 標(biāo)記垃圾對(duì)象
-
清理垃圾對(duì)象
-
新生代:基于復(fù)制
- Eden
- from Survivor
- to Survivor
老年代:基于標(biāo)記清除、標(biāo)記整理
-
-
-
垃圾收集器
-
新生代
觸發(fā)MinorGC(Young GC)
虛擬機(jī)在進(jìn)行minorGC之前會(huì)判斷老年代最大的可用連續(xù)空間是否大于新生代的所有對(duì)象總空間 1、如果大于的話(huà),直接執(zhí)行minorGC 2、如果小于,判斷是否開(kāi)啟HandlerPromotionFailure,沒(méi)有開(kāi)啟直接FullGC 3、如果開(kāi)啟了HanlerPromotionFailure, JVM會(huì)判斷老年代的最大連續(xù)內(nèi)存空間是否大于歷次晉升的大小,如果小于直接執(zhí)行FullGC 4、如果大于的話(huà),執(zhí)行minorGC-
Serial
-
參數(shù)
- -XX:SurvivorRatio
- -XX:PretenureSizeThreshold
- -XX:HandlePromotionFailure
組合:CMS、Serial Old
-
-
ParNew
-
參數(shù)
- -XX:SurvivorRatio
- -XX:PretenureSizeThreshold
- -XX:HandlePromotionFailure
- -XX:ParallelGCThreads
組合:CMS、Serial Old
-
-
Parellel Scavenge
特點(diǎn):關(guān)注吞吐量 用戶(hù)代碼運(yùn)行時(shí)間/(GC + 用戶(hù)... )
-
參數(shù)
- -XX:MaxGCPauseMillis(通過(guò)調(diào)整新生代大小,比如調(diào)小,時(shí)間減少,但GC更頻繁,吞吐量下降)
- -XX:GCTimeRatio
- -XX:+UseAdaptiveSizePolicy(開(kāi)啟后,JVM自動(dòng)調(diào)整新生代大小,SurvivorRatio,晉升老年代年齡等參數(shù)實(shí)現(xiàn)最大吞吐量)
組合:Serial Old、Parallel Old
-
-
老年代
觸發(fā)FullGC
老年代空間不足
如果創(chuàng)建一個(gè)大對(duì)象,Eden區(qū)域當(dāng)中放不下這個(gè)大對(duì)象,會(huì)直接保存在老年代當(dāng)中,如果老年代空間也不足,就會(huì)觸發(fā)Full GC。為了避免這種情況,最好就是不要?jiǎng)?chuàng)建太大的對(duì)象。持久代空間不足
如果有持久代空間的話(huà),系統(tǒng)當(dāng)中需要加載的類(lèi),調(diào)用的方法很多,同時(shí)持久代當(dāng)中沒(méi)有足夠的空間,就出觸發(fā)一次Full GCYGC出現(xiàn)promotion failure
promotion failure發(fā)生在Young GC, 如果Survivor區(qū)當(dāng)中存活對(duì)象的年齡達(dá)到了設(shè)定值,會(huì)就將Survivor區(qū)當(dāng)中的對(duì)象拷貝到老年代,如果老年代的空間不足,就會(huì)發(fā)生promotion failure, 接下去就會(huì)發(fā)生Full GC.統(tǒng)計(jì)YGC發(fā)生時(shí)晉升到老年代的平均總大小大于老年代的空閑空間
在發(fā)生YGC是會(huì)判斷,是否安全,這里的安全指的是,當(dāng)前老年代空間可以容納YGC晉升的對(duì)象的平均大小,如果不安全,就不會(huì)執(zhí)行YGC,轉(zhuǎn)而執(zhí)行Full GC。顯示調(diào)用System.gc
-
CMS
特點(diǎn):關(guān)注響應(yīng)時(shí)間
過(guò)程:初始標(biāo)記,并發(fā)標(biāo)記,重新標(biāo)記,并發(fā)清理
-
CPU占用問(wèn)題
并發(fā)清理階段,線(xiàn)程數(shù)默認(rèn)為 (CPU數(shù)量+3/4),即占用不少于25%的CPU資源,當(dāng)CPU數(shù)量降低時(shí)占用更多。
-
浮動(dòng)垃圾問(wèn)題:并發(fā)清理階段,預(yù)留空間給用戶(hù)線(xiàn)程使用,產(chǎn)生的垃圾下次GC處理,若空間不足,使用Serial Old重新回收Old區(qū),導(dǎo)致停頓時(shí)間過(guò)長(zhǎng)
為了在并發(fā)清理階段不STW,需要預(yù)留部分內(nèi)存用于并發(fā)清理階段的用戶(hù)線(xiàn)程使用,JDK1.5默認(rèn)在老年代68%時(shí)啟動(dòng)并發(fā)清理,JDK1.6提升為92%,通過(guò)-XX:CMSInitiatingOccupancyFraction可設(shè)置。當(dāng)預(yù)留的內(nèi)存不足時(shí),會(huì)出現(xiàn)一次“Concurrent Mode Failure”,使用Serial Old會(huì)暫停用戶(hù)線(xiàn)程重新收集old區(qū)。
垃圾碎片問(wèn)題:CMS采用“標(biāo)記-清除”算法,可能存在空間足夠,卻沒(méi)有連續(xù)可用的空間存放對(duì)象,觸發(fā)full gc,可通過(guò)參數(shù)控制full-gc時(shí)整理內(nèi)存碎片
Serial Old(MSC)
Parallel Old
-
-
G1
-
回收周期
年輕代GC
-
混合GC
觸發(fā)時(shí)機(jī):
當(dāng)老年代分區(qū)占用總堆比例超過(guò)閾值(默認(rèn)45%)時(shí),觸發(fā)混合GC。
初始標(biāo)記:和下一次年輕代GC一起,STW并行標(biāo)記,收集所有的GC Roots。
并發(fā)標(biāo)記:多線(xiàn)程并發(fā)協(xié)同標(biāo)示存活對(duì)象圖。
重新標(biāo)記:STW并行重新標(biāo)記上個(gè)階段產(chǎn)生的新垃圾。
并行回收:垃圾清理。
-
-
- Full GC
觸發(fā)時(shí)機(jī):對(duì)象分配失敗時(shí)?
使用串行垃圾收集器對(duì)整個(gè)堆全面壓縮。
G1的設(shè)計(jì)目標(biāo)通過(guò)不斷調(diào)優(yōu)而不再需要full GC。
- 堆空間調(diào)整
JVM通過(guò)在Xms和Xmx之間動(dòng)態(tài)調(diào)整堆大小及年輕代大小,以滿(mǎn)足用戶(hù)設(shè)置的GC暫停時(shí)間MaxGCPauseMillis和GCTimeRatio(用戶(hù)線(xiàn)程時(shí)間/GC線(xiàn)程時(shí)間)的目標(biāo)。
- RSet
- 引用關(guān)系

- PRT 粒度?
- 關(guān)系維護(hù)
維護(hù)時(shí)機(jī):
對(duì)象引用關(guān)系變化時(shí)(包括引用賦值、GC移動(dòng)對(duì)象等),觸發(fā)寫(xiě)柵欄代碼,維護(hù)Rset。
若發(fā)生一個(gè)跨區(qū)引用關(guān)系變化,G1垃圾收集器會(huì)將相應(yīng)的card加入到“臟卡片隊(duì)列”?!安l(fā)優(yōu)化線(xiàn)程”會(huì)掃描隊(duì)列中的卡片來(lái)更新RSet。當(dāng)“并發(fā)優(yōu)化線(xiàn)程”來(lái)不及處理不過(guò)來(lái)時(shí),會(huì)掛起用戶(hù)線(xiàn)程,讓用戶(hù)線(xiàn)程也加入到更新Rset。
- 全局卡片表

在任意收集周期,掃描Rset與PRT時(shí),會(huì)將掃描到的引用記錄標(biāo)記到全局卡片表,避免重復(fù)掃描。在收集周期的最后將該表清空,顯示為Clear CT。
- 工作竊取機(jī)制
- 收集活動(dòng)圖

深入并發(fā)
Java內(nèi)存模型
-
模型簡(jiǎn)圖
模型簡(jiǎn)圖 -
內(nèi)存操作
- lock&unlock
- use&load&read
- assign&store&write
-
happens before
- 用途:呈現(xiàn)給程序員的并發(fā)視圖,闡述操作之間的內(nèi)存可見(jiàn)性。
-
與JMM的關(guān)系
與JMM的關(guān)系
-
問(wèn)題:重排序
目的:提高執(zhí)行效率
-
類(lèi)型
-
編譯器重排
JMM “happens before” 規(guī)則確保編譯器禁止特定的編譯器重排。
例子:局部變量和操作數(shù)棧的關(guān)系
-
普通讀寫(xiě)/volatile讀寫(xiě)
普通讀寫(xiě)/volatile讀寫(xiě)- 當(dāng)?shù)诙€(gè)操作是volatile寫(xiě)時(shí),不管第一個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile寫(xiě)之前的操作不會(huì)被編譯器重排序到volatile寫(xiě)之后
- 當(dāng)?shù)谝粋€(gè)操作是volatile讀時(shí),不管第二個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile讀之后的操作不會(huì)被編譯器重排序到volatile讀之前
- 當(dāng)?shù)谝粋€(gè)操作是volatile寫(xiě),第二個(gè)操作是volatile讀時(shí),不能重排序
-
final域
-
寫(xiě)
- JMM禁止編譯器把final域的寫(xiě)重排序到構(gòu)造函數(shù)之外(確保在return之前)
-
讀
- none
-
-
處理器重排序
-
類(lèi)型
-
指令并行重排
- 指令并行執(zhí)行,改變沒(méi)有數(shù)據(jù)依賴(lài)性的指令執(zhí)行順序
-
內(nèi)存系統(tǒng)重排
- 例子:cpu緩存的執(zhí)行結(jié)果,未及時(shí)同步到內(nèi)存,導(dǎo)致順序錯(cuò)亂
-
-
內(nèi)存屏障
-
類(lèi)型
-
LoadLoad
- 序列:Load1,Loadload,Load2
- 作用:確保Load1所要讀入的數(shù)據(jù)能夠在被Load2和后續(xù)的Load指令訪問(wèn)前讀入
-
StoreStore
- 序列:Store1,StoreStore,Store2
- 確保Store1的數(shù)據(jù)在Store2以及后續(xù)Store指令操作相關(guān)數(shù)據(jù)之前對(duì)其它處理器可見(jiàn)(例如向主存刷新數(shù)據(jù)
-
LoadStore
- 序列: Load1; LoadStore; Store2
- 確保Load1的數(shù)據(jù)在Store2和后續(xù)Store指令被刷新之前讀取
-
StoreLoad
- 序列: Store1; StoreLoad; Load2
- 確保Store1的數(shù)據(jù)在被Load2和后續(xù)的Load指令讀取之前對(duì)其他處理器可見(jiàn)
”Enter”與”Load”相同,”Exit”與”Store”相同,除非被原子指令的使用和特性覆蓋
-
-
使用對(duì)象
-
volatile
-
讀
- volatile讀
- LoadLoad
- LoadStore
-
寫(xiě)
- StoreStore
- volatile寫(xiě)
- StoreLoad
-
-
synchronized
-
進(jìn)入管程
- monitor enter 等價(jià)于 volatile讀
- LoadLoad(EnterLoad)
- LoadStore(EnterStore)
-
退出管程
- LoadStore(LoadExit)
- StoreStore(StoreExit)
- monitor exit 等價(jià)于 volatile寫(xiě)
- StoreLoad(ExitEnter)
-
-
CAS
- 同時(shí)具有volatile讀和寫(xiě)的雙內(nèi)存語(yǔ)義
-
final
-
寫(xiě)
- 構(gòu)造函數(shù)開(kāi)始
- 對(duì)象的final域?qū)慭對(duì)象的final域的成員域?qū)懀⊿tore)
- StoreStore(return之前)
- 構(gòu)造函數(shù)return
- 將對(duì)象的引用賦值給引用變量obj(Store)
-
讀
- 初次讀引用變量obj(Load)
- LoadLoad(讀final域之前)
-
初次讀引用變量obj指向?qū)ο蟮膄inal域\初次讀引用變量obj指向?qū)ο蟮膄inal域的成員域(Load)
-
-
-
-
-
-
Java關(guān)鍵字內(nèi)存模型語(yǔ)義
-
volatile
對(duì)volatile變量的讀寫(xiě)操作,在語(yǔ)義上和synchronized臨界區(qū)一致,即臨界區(qū)內(nèi)的操作具有原子性,臨界區(qū)前的操作對(duì)臨界區(qū)可見(jiàn),臨界區(qū)內(nèi)的操作對(duì)臨界區(qū)后可見(jiàn)。
-
volatile操作具有原子性,包括對(duì)于64位的double或long類(lèi)型變量。
可見(jiàn)性:對(duì)一個(gè)volatile變量的讀,總是能看到(任意線(xiàn)程)對(duì)這個(gè)volatile變量最后的寫(xiě)入。
原子性:對(duì)任意單個(gè)volatile變量的讀/寫(xiě)具有原子性,但類(lèi)似于volatile++這種復(fù)合操作不具有原子性。
volatile引用或數(shù)組,volatile只能保證引用和數(shù)組引用的可見(jiàn)性,對(duì)于引用指向的對(duì)象、對(duì)象的屬性、數(shù)組的元素,均不能保證可見(jiàn)性。
- synchronized
pre code A...
sync{
臨界區(qū) B...
}
after code C...
順序一致性,通過(guò)臨界區(qū)前后插入蔽障,保證三個(gè)區(qū)域代碼順序執(zhí)行,A happens before B,B happens before C...,但ABC區(qū)域內(nèi)部允許重排序。
- final
-
concurrent包\AQS框架
-
通用模式
- 1.聲明volatile共享變量
- 2.使用CAS的原子條件更新來(lái)實(shí)現(xiàn)線(xiàn)程之間的同步
- 3.配合以volatile的讀/寫(xiě)和CAS所具有的volatile讀/寫(xiě)的內(nèi)存語(yǔ)義來(lái)實(shí)現(xiàn)線(xiàn)程之間的通信
-
實(shí)現(xiàn)示意圖
實(shí)現(xiàn)示意圖
-
java關(guān)鍵字功能、性能
-
Volatile
保證可見(jiàn)性
-
內(nèi)存屏障,避免JVM優(yōu)化重排序指令帶來(lái)的并法問(wèn)題
每次對(duì)volatile變量寫(xiě)之后,編譯器會(huì)在之后插入一條匯編指令,將本CPU內(nèi)存刷到共享內(nèi)存,同時(shí)使其他CPU內(nèi)存無(wú)效化;該操作要求對(duì)volatile寫(xiě)之前的指令全部已經(jīng)完成,所以編譯器不能重排到寫(xiě)之后;
不是線(xiàn)程安全的,可與CAS結(jié)合使用,實(shí)現(xiàn)線(xiàn)程安全
-
Synchronized
- 偏向鎖--基于鎖總是由同一線(xiàn)程獲得,完全消除鎖開(kāi)銷(xiāo)
- 輕量級(jí)鎖--基于在沒(méi)有多線(xiàn)程競(jìng)爭(zhēng)的情況下,通過(guò)CAS自旋代替互斥
- 重量級(jí)鎖--基于互斥鎖,沒(méi)搶到鎖即阻塞
-
ReentrantLock
-
可讓等待線(xiàn)程放棄等待,執(zhí)行其它任務(wù)
調(diào)用 可中斷獲取 方法,如果獲取失敗,會(huì)進(jìn)入隊(duì)列,自旋獲取鎖并檢查中斷標(biāo)志,當(dāng)中斷標(biāo)志被置位TRUE,拋出 中斷異常,并取消獲取鎖。
提供公平鎖
一個(gè)鎖可綁定多個(gè)條件變量
-
CPU緩存帶來(lái)的問(wèn)題
-
簡(jiǎn)單示意圖
image.pngCPU Cache分成了三個(gè)級(jí)別: L1, L2, L3. 級(jí)別越小越接近CPU, 所以速度也更快, 同時(shí)也代表著容量越小. L1是最接近CPU的, 它容量最小, 例如32K, 速度最快,每個(gè)核上都有一個(gè)L1 Cache(準(zhǔn)確地說(shuō)每個(gè)核上有兩個(gè)L1 Cache, 一個(gè)存數(shù)據(jù) L1d Cache, 一個(gè)存指令 L1i Cache). L2 Cache 更大一些,例如256K, 速度要慢一些, 一般情況下每個(gè)核上都有一個(gè)獨(dú)立的L2 Cache; L3 Cache是三級(jí)緩存中最大的一級(jí),例如12MB,同時(shí)也是最慢的一級(jí), 在同一個(gè)CPU插槽之間的核共享一個(gè)L3 Cache.
-
緩存行(cache line)
緩存存取粒度單位,一般64字節(jié)。
通過(guò)命令查看:
cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
64一個(gè)Java long型占8字節(jié), 所以從一條緩存行上你可以獲取到8個(gè)long型變量. 所以如果你訪問(wèn)一個(gè)long型數(shù)組, 當(dāng)有一個(gè)long被加載到cache中, 你將無(wú)消耗地加載了另外7個(gè). 所以你可以非常快地遍歷數(shù)組.
perf工具抓取L1cache未命中數(shù)據(jù):
$ perf stat -e L1-dcache-load-misses java L1CacheMiss
-
緩存一致性協(xié)議(MESI)
http://blog.csdn.net/opensure/article/details/46669337
簡(jiǎn)單總結(jié):
同一個(gè)cache line可以被緩存在多個(gè)核,當(dāng)核A修改該cache line時(shí),會(huì)導(dǎo)致其他核的cache line無(wú)效(注:1),當(dāng)其他核需要修改該cache line時(shí),會(huì)強(qiáng)制核A將修改的cache line刷回內(nèi)存,然后其他核重新讀取,以此保證一個(gè)cache line只能同時(shí)被一個(gè)核修改。注:1
每個(gè)處理器通過(guò)嗅探在總線(xiàn)上傳播的數(shù)據(jù)來(lái)檢查自己緩存的值是不是過(guò)期了,當(dāng)處理器發(fā)現(xiàn)自己緩存行對(duì)應(yīng)的內(nèi)存地址被修改,就會(huì)將當(dāng)前處理器的緩存行設(shè)置成無(wú)效狀態(tài)。-
帶來(lái)的問(wèn)題:偽共享
多線(xiàn)程程序需要并發(fā)操作volatile變量A和B時(shí),如果AB屬于同一個(gè)cache line,由于MESI協(xié)議,導(dǎo)致同一時(shí)刻只能有一個(gè)線(xiàn)程操作A或B中的一個(gè)變量,影響多線(xiàn)程程序的并發(fā)性能。
解決:可在AB之間填充無(wú)效變量,使AB處于不同的cache line,解決沖突。
Lock前綴指令會(huì)引起處理器緩存回寫(xiě)到內(nèi)存。Lock前綴指令導(dǎo)致在執(zhí)行指令期間,聲言處理器的 LOCK# 信號(hào)。在多處理器環(huán)境中,LOCK# 信號(hào)確保在聲言該信號(hào)期間,處理器可以獨(dú)占使用任何共享內(nèi)存。(因?yàn)樗鼤?huì)鎖住總線(xiàn),導(dǎo)致其他CPU不能訪問(wèn)總線(xiàn),不能訪問(wèn)總線(xiàn)就意味著不能訪問(wèn)系統(tǒng)內(nèi)存),但是在最近的處理器里,LOCK#信號(hào)一般不鎖總線(xiàn),而是鎖緩存,畢竟鎖總線(xiàn)開(kāi)銷(xiāo)比較大。在8.1.4章節(jié)有詳細(xì)說(shuō)明鎖定操作對(duì)處理器緩存的影響,對(duì)于Intel486和Pentium處理器,在鎖操作時(shí),總是在總線(xiàn)上聲言LOCK#信號(hào)。但在P6和最近的處理器中,如果訪問(wèn)的內(nèi)存區(qū)域已經(jīng)緩存在處理器內(nèi)部,則不會(huì)聲言LOCK#信號(hào)。相反地,它會(huì)鎖定這塊內(nèi)存區(qū)域的緩存并回寫(xiě)到內(nèi)存,并使用緩存一致性機(jī)制來(lái)確保修改的原子性,此操作被稱(chēng)為“緩存鎖定”,緩存一致性機(jī)制會(huì)阻止同時(shí)修改被兩個(gè)以上處理器緩存的內(nèi)存區(qū)域數(shù)據(jù)。
一個(gè)處理器的緩存回寫(xiě)到內(nèi)存會(huì)導(dǎo)致其他處理器的緩存無(wú)效。IA-32處理器和Intel 64處理器使用MESI(修改,獨(dú)占,共享,無(wú)效)控制協(xié)議去維護(hù)內(nèi)部緩存和其他處理器緩存的一致性。在多核處理器系統(tǒng)中進(jìn)行操作的時(shí)候,IA-32 和Intel 64處理器能嗅探其他處理器訪問(wèn)系統(tǒng)內(nèi)存和它們的內(nèi)部緩存。它們使用嗅探技術(shù)保證它的內(nèi)部緩存,系統(tǒng)內(nèi)存和其他處理器的緩存的數(shù)據(jù)在總線(xiàn)上保持一致。例如在Pentium和P6 family處理器中,如果通過(guò)嗅探一個(gè)處理器來(lái)檢測(cè)其他處理器打算寫(xiě)內(nèi)存地址,而這個(gè)地址當(dāng)前處理共享狀態(tài),那么正在嗅探的處理器將無(wú)效它的緩存行,在下次訪問(wèn)相同內(nèi)存地址時(shí),強(qiáng)制執(zhí)行緩存行填充。
-
鎖的一種實(shí)現(xiàn)
-
Synchronized
- 可重入
Lock
public class Counter{
public class Lock{
private boolean isLocked = false;
public synchronized void lock()
throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
-
ReentrantLock
-
非公平
public class Lock{
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
-
public synchronized void lock()
throws InterruptedException{
Thread callingThread =
Thread.currentThread();
while(isLocked && lockedBy != callingThread){
wait();
}
isLocked = true;
lockedCount++;
lockedBy = callingThread;
}
public synchronized void unlock(){
if(Thread.curentThread() ==
this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
notify();
}
}
}
...
}
- 公平
public class FairLock {
private boolean isLocked = false;
private Thread lockingThread = null;
private List<QueueObject> waitingThreads =
new ArrayList<QueueObject>();
public void lock() throws InterruptedException{
QueueObject queueObject = new QueueObject();
boolean isLockedForThisThread = true;
synchronized(this){
waitingThreads.add(queueObject);
}
while(isLockedForThisThread){
synchronized(this){
isLockedForThisThread =
isLocked || waitingThreads.get(0) != queueObject;
if(!isLockedForThisThread){
isLocked = true;
waitingThreads.remove(queueObject);//加到鎖的線(xiàn)程移除喚醒對(duì)象
lockingThread = Thread.currentThread();
return;
}
}
try{
queueObject.doWait();
}catch(InterruptedException e){
synchronized(this) { waitingThreads.remove(queueObject); }
throw e;
}
}
}
public synchronized void unlock(){
if(this.lockingThread != Thread.currentThread()){
throw new IllegalMonitorStateException(
"Calling thread has not locked this lock");
}
isLocked = false;
lockingThread = null;
if(waitingThreads.size() > 0){
waitingThreads.get(0).doNotify();
}
}
}
public class QueueObject {
private boolean isNotified = false;
public synchronized void doWait() throws InterruptedException {
while(!isNotified){
this.wait();
}
this.isNotified = false;
}
public synchronized void doNotify() {
this.isNotified = true;
this.notify();
}
public boolean equals(Object o) {
return this == o;
}
}
-
ReadWriteLock
public class ReadWriteLock{
private int readers = 0;
private int writers = 0;
private int writeRequests = 0;
public synchronized void lockRead()
throws InterruptedException{
while(writers > 0 || writeRequests > 0){
wait();
}
readers++;
}
public synchronized void unlockRead(){
readers--;
notifyAll();
}
public synchronized void lockWrite()
throws InterruptedException{
writeRequests++;
while(readers > 0 || writers > 0){
wait();
}
writeRequests--;
writers++;
}
public synchronized void unlockWrite()
throws InterruptedException{
writers--;
notifyAll();
}
}
并發(fā)集合、線(xiàn)程池
-
線(xiàn)程池-ThreadPoolExecutor
-
配置
-
數(shù)量配置
-
根據(jù)任務(wù)性質(zhì)
- cpu密集型--N+1
- IO密集型--2N
- 混合型--如果cpu消耗和IO相當(dāng),拆分為cpu密集型和IO密集型
-
-
任務(wù)隊(duì)列配置
- 任務(wù)有優(yōu)先級(jí)--采用PriorityBlockingQueue
-
-
-
并發(fā)集合
-
ConcurrentModificationException
Iterator內(nèi)部維護(hù)一個(gè)expectedModCount,當(dāng)直接通過(guò)集合進(jìn)行修改時(shí),集合的modCount會(huì)改變,但expectedModCount不會(huì)改變,當(dāng)Iterator調(diào)用hasNext()和Next()時(shí),會(huì)比較modCount==expectedModCount,如果不等,說(shuō)明集合被外部修改,拋出異常
-
ConcurrentHashMap
-
分段鎖
size,未避免成為熱點(diǎn)域,每個(gè)鎖維護(hù)鎖范圍內(nèi)的bucket的計(jì)數(shù),size()函數(shù)返回所有計(jì)數(shù)之和,因?yàn)閟ize()函數(shù)不會(huì)同時(shí)加上所有的鎖,返回的值不精確,isEmpty()同理。
-
弱一致迭代器
當(dāng)?shù)呀?jīng)開(kāi)始,集合元素被修改,若被刪除,迭代到該元素時(shí),next()不會(huì)返回該元素;若增加了元素,next()可能返回該元素,也可能不。
-
隊(duì)列的同步問(wèn)題
-
阻塞隊(duì)列
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- DelayQueue
- SynchronousQueue
- LinkedTransferQueue
- LinkedBlockingDeque
-
零散知識(shí)點(diǎn)
常用命令
-
jps
顯示當(dāng)前所有java進(jìn)程pid
-
jmap
主要用于打印指定Java進(jìn)程(或核心文件、遠(yuǎn)程調(diào)試服務(wù)器)的共享對(duì)象內(nèi)存映射或堆內(nèi)存細(xì)節(jié)
-
jstack
查看線(xiàn)程信息
-
jstat
主要利用JVM內(nèi)建的指令對(duì)Java應(yīng)用程序的資源和性能進(jìn)行實(shí)時(shí)的命令行的監(jiān)控,包括了對(duì)Heap size和垃圾回收狀況的監(jiān)控
jhat
jinfo?
-
javap?
反編譯
String
-
intern()
jdk6.0:
常量池存在于方法區(qū),和堆完全隔離,常量池只能存放字符串,intern()方法將字符串放入常量池,并返回指向方法區(qū)常量池相應(yīng)字符串的引用;若字符串已存在,返回已存在字符串的引用。
jdk7.0:
常量池存在于堆,常量池可存放指向堆內(nèi)String Obj的引用,string_obj.intern()方法將指向string_obj的引用存入常量池,并返回指向string_obj的引用;若字符串已存在,返回已存在字符串的引用;檢查常量池字符串是否存在時(shí),池內(nèi)引用指向的string_obj的值也要算作存在。String s = new String("s");//創(chuàng)建兩個(gè)對(duì)象,一個(gè)堆內(nèi)string_obj,一個(gè)常量池內(nèi)字符串;
String s = new String("s1") + new String("s2");//創(chuàng)建五個(gè)對(duì)象,堆內(nèi)string_obj_s("s1s2"),string_obj("s1"),string_obj("s2"),常量池字符串"s1","s2".
String s = "test";//該字符串直接插入常量池; -
substring()
-
JDK6
-
String Obj
- char value[]
- int offset
- int count
實(shí)現(xiàn):以char[]為入?yún)ew String(),String Obj引用傳入的char [];substring產(chǎn)生一個(gè)新的String Obj,引用源String的value,通過(guò)改變offset和count實(shí)現(xiàn)。
缺點(diǎn):子串一直拿著父串的引用,導(dǎo)致父不能釋放內(nèi)存
解決:通過(guò)String sub = s.substring() + "";返回一個(gè)包含新value的串
-
-
JDK7
-
String Obj
- char value[]
解決JDK6問(wèn)題:substring()直接返回一個(gè)新的數(shù)組
-
-
equals() & hashcode()
反射&代理&注解
-
反射
通過(guò)反射API,在運(yùn)行狀態(tài)獲取類(lèi)的所有屬性和方法,構(gòu)造對(duì)象,并訪問(wèn)屬性和方法。
public static Class<?> forName(String className)
throws ClassNotFoundException
public Method[] getMethods()
throws SecurityException
public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException -
動(dòng)態(tài)代理
-
例子
功能:禁止使用List接口中的add方法。
public List getList(final List list) {
return (List) Proxy.newProxyInstance(DummyProxy.class.getClassLoader(), new Class[] { List.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("add".equals(method.getName())) {
throw new UnsupportedOperationException();
}
else {
return method.invoke(list, args);
}
}
});
}
-
- 簡(jiǎn)單原理
JVM創(chuàng)建一個(gè)代理類(lèi):
public final class $Proxy0 extends Proxy implements List
代理類(lèi)保存InvocationHandler對(duì)象:
public $Proxy(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
代理類(lèi)實(shí)現(xiàn)接口:
public final List_Method()
{
super.h.invoke(this, m3, null);
// 實(shí)際上就是調(diào)用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法
}
-
注解
-
注解定義
@Target(ElementType.METHOD) //可注解范圍,方法
@Retention(RetentionPolicy.RUNTIME) //注解有效范圍,運(yùn)行時(shí)有效
public @interface MyAnnotation
{
String myKey() default "hello"; //定義一個(gè)注解屬性,類(lèi)型為String,默認(rèn)值為“hello”
} -
注解實(shí)施
@MyAnnotation(myKey="nihao")
public void method()
{
...
} -
注解處理器
注解本身不會(huì)對(duì)代碼造成影響,需要注解處理器解析注解,實(shí)現(xiàn)注解功能。
使用反射API:
使用了注解的Class->Class中使用了注解的方法和屬性->方法和屬性的注解->注解值->實(shí)現(xiàn)自定義注解功能;public class myAnnotationHandler
{
public static void processAnnotations(Object obj)
{
Class<?> class = obj.getClass();
for (Method m : class.getDeclaredMethods() )
{
MyAnnotation myAnnotation = m.getAnnotation(MyAnnotation.class);
if (myAnnotation != null)
{
String annotationKey = myAnnotation.myKey();
...
//實(shí)現(xiàn)自定義注解功能
}
}
}
}
-
序列化
1、在Java中,只要一個(gè)類(lèi)實(shí)現(xiàn)了java.io.Serializable接口,那么它就可以被序列化。
2、通過(guò)ObjectOutputStream和ObjectInputStream對(duì)對(duì)象進(jìn)行序列化及反序列化
3、虛擬機(jī)是否允許反序列化,不僅取決于類(lèi)路徑和功能代碼是否一致,一個(gè)非常重要的一點(diǎn)是兩個(gè)類(lèi)的序列化 ID 是否一致(就是 private static final long serialVersionUID)
4、序列化并不保存靜態(tài)變量。
5、要想將父類(lèi)對(duì)象也序列化,就需要讓父類(lèi)也實(shí)現(xiàn)Serializable 接口。
6、Transient 關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient 變量的值被設(shè)為初始值,如 int 型的是 0,對(duì)象型的是 null。
7、服務(wù)器端給客戶(hù)端發(fā)送序列化對(duì)象數(shù)據(jù),對(duì)象中有一些數(shù)據(jù)是敏感的,比如密碼字符串等,希望對(duì)該密碼字段在序列化時(shí),進(jìn)行加密,而客戶(hù)端如果擁有解密的密鑰,只有在客戶(hù)端進(jìn)行反序列化時(shí),才可以對(duì)密碼進(jìn)行讀取,這樣可以一定程度保證序列化對(duì)象的數(shù)據(jù)安全。
- 用途:對(duì)象持久化(下盤(pán)及網(wǎng)絡(luò)傳輸)





