JVM基本結(jié)構(gòu)
PC寄存器
- 每個線程擁有一個PC寄存器
- 線程創(chuàng)建時創(chuàng)建PC寄存器
- 指向下一條指令的地址
- 執(zhí)行本地方法時巴帮,PC的值是undefind
方法區(qū):用來保存類的一些元信息的(例外意敛,jdk1.7String的常量信息已經(jīng)移到了堆)
- 類的常量池
- 字段方法信息
- 方法字節(jié)碼
- 通常和永久區(qū)(Perm關(guān)聯(lián)在一起)
永久區(qū)并不是永久不變的畅铭,只是相對的穩(wěn)定
堆
- 和程序開發(fā)密切相關(guān)
- 應用程序?qū)ο蠖急4嬖趈ava堆中
- 所有線程共享java堆
- 對于GC來說卷雕,堆也是分代的
- GC的主要工作區(qū)間
桟:
- 線程私有的
- 由一系列幀組成(因此java桟也叫做幀桟)
- 幀保存一個方法的局部變量介汹,操作數(shù)桟棍辕,常量池指針
-
每一次方法調(diào)用創(chuàng)建一個幀,并壓桟
reference this 類比于python中的成員方法必須傳self
操作數(shù)桟
桟上分配
- 小對象(一般幾十個bytes)席函,在沒有逃逸(這個對象在其他線程也會使用到)的情況下铐望,可以直接分配在桟上。
- 直接分配在桟上茂附,可以自動回收正蛙,減輕GC壓力
- 大對象或者逃逸對象無法再桟上分配。
堆营曼、桟乒验、方法區(qū)的交互
java的內(nèi)存模型
- 每個線程中有一個工作內(nèi)存和主存獨立
- 工作內(nèi)存存放主存中變量的拷貝
- 可見性:一個線程修改了某個變量,其他線程立即知道
- 有序性:
在本線程內(nèi)蒂阱,操作都是有序的
在線程外觀察徊件,操作都是無序的(指令重排或主內(nèi)存同步延時)
編譯和解釋運行的概念
- 解釋執(zhí)行:
解釋執(zhí)行以解釋方式運行字節(jié)碼
解釋執(zhí)行的意思是:讀一句執(zhí)行一句 - 編譯運行(JIT):
將字節(jié)碼編譯成機器碼
直接執(zhí)行機器碼
運行時編譯
編譯后性能有數(shù)量級的提升
GC的概念
- 全稱Garbage Collection
- 1960 List語言首先使用GC
- java中奸攻,GC管理堆空間和永久區(qū)
GC算法
引用計數(shù)法
- 概念:通過引用計算來回收垃圾
- 使用者:COM,Python虱痕,ActionScript3
- 原理:
引用計數(shù)器的實現(xiàn)很簡單,對于一個對象A,只要有任何一個對象引用了A,則A的引用計數(shù)器就 加1,當引用失效時,引用計數(shù)器就減1睹耐。只要對象A的引用計數(shù)器的值為0,則對象A就不可能再被使用。 - 缺點:引用和去引用伴隨著加法和減法部翘,影響性能
-
很難處理循環(huán)引用(垃圾對象的循環(huán)引用硝训,也就是那三個點不會被回收)
標記清除法
- 原理:
將垃圾回收分為兩個階段:標記階段和清除階段。在標記階段新思,首先通過根節(jié)點窖梁,標記所有從根節(jié)點開始的可達對象,因此夹囚,未標記對象就是未引用的垃圾對象纵刘,然后在清除階段清除所有未標記的對象。 - 適用場合:適用于存活對象較多的場合荸哟,如老年代假哎。
標記壓縮法
- 原理
它在標記清除法的基礎(chǔ)上做了一些優(yōu)化。和標記清除法一樣鞍历,標記壓縮算法也是從根節(jié)點開始舵抹,對所有的對象做一次標記。但之后劣砍,它并不是簡單的清理未標記的對象惧蛹,而是將所有的的存活對象壓縮到內(nèi)存的一端,之后清理邊界外的所有空間刑枝。 - 優(yōu)勢:
能夠整理內(nèi)存碎片香嗓,避免分配大對象時,空間不夠?qū)е翭ullGC - 適用場合:適用于存活對象較多的場合装畅,如老年代陶缺。
復制算法
- 原理:
將原有的內(nèi)存分為兩塊,每次只是用其中一塊洁灵,在垃圾回收時饱岸,將正在使用的內(nèi)存中的存活對象復制到未使用的內(nèi)存塊中,之后清除正在使用的內(nèi)存塊中的所有對象徽千,交換兩個內(nèi)存的角色苫费,完成垃圾回收 - 優(yōu)點:與標記清除法相比,復制算法是一種相對高效的回收算法
- 缺點:浪費空間
- 適用場合:適用于存活對象較少的場合双抽,如新生代百框。
復制算法的優(yōu)化:整合標記清除思想
- 當垃圾回收進行的時候,大對象進入擔彪剐冢空間(因為復制空間一般不大)
- 老年對象(好幾次回收都沒有被回收掉的對象)進入老年代
可觸及性
可觸及的:
從根節(jié)點可以觸及到這個對象
可復活的:
一旦所有引用被釋放铐维,就是可復活狀態(tài)柬泽,因為在finalize()中可能復活該對象
不可觸及
- 在finalize()之后,可能會進入不可觸及狀態(tài)
- 不可觸及狀態(tài)不可能復活
-
可以回收
第一次GC調(diào)用finalize()方法嫁蛇,因為覆寫了finalize()方法锨并,復活了obj,所以第一次obj不是nul睬棚,finalize()方法只會被調(diào)用一次第煮,第二次gc,obj就不能復活了
根
- 桟中引用的對象
- 方法區(qū)中靜態(tài)成員或者常量的引用 (全局對象)
- JNI方法桟中的引用對象
Stop-The-World
- java中的一種全局停頓現(xiàn)象
- 全局停頓抑党,所有java代碼停止包警,native可以執(zhí)行,但不能和jvm交互
- 多半由于GC引起
- 原因:類比一邊開party底靠,一邊打掃衛(wèi)生害晦,只能讓大家停下來才能掃干凈。
- 危害:長時間服務停止暑中,沒有響應壹瘟;遇到HA系統(tǒng),可能引起主備切換痒芝,嚴重危害環(huán)境。
GC參數(shù)
串行收集器(GC線程只有一個)
最古老牵素,最穩(wěn)定严衬,效率高,可能產(chǎn)生較長的停頓
-XX:+UseSerialGC:
新生代笆呆、老年代使用串行回收
新生代使用復制算法请琳,老年代使用標記壓縮算法
并行收集器(GC線程有多個)
ParNew:
- -XX:+UseParNewGC:
- 新生代并行,老年代還是串行
- -XX:+ParallelGCThreads 限制線程數(shù)量
Parallel收集器
- 類似ParNew
- 新生代復制算法
- 老年代標記壓縮算法
- 更加關(guān)注吞吐量
- -XX:+UseParallelGC
老年代串行 - -XX:+UseParallelOldGC
老年代并行 - XX:MaxGCPauseMills
最大停頓時間 - XX:GCTimeRatio
0-100取值范圍
垃圾收集時間占總時間比
默認99,即最大允許1%時間做GC - 兩個參數(shù)是矛盾的。因為停頓時間和吞吐量不可能同時調(diào)優(yōu)
CMS收集器
- Concurrent Mark Sweep 并發(fā)標記清除(并發(fā)指的是和應用程序一起執(zhí)行)
- 標記清除算法
- 并發(fā)階段會降低吞吐量
- 老年代收集器(新生代使用ParNew)
- XX:+UseConcMarkSweepGC
-
停頓時間較少
-過程比較復雜
初始標記:根可以直接關(guān)聯(lián)到的對象赴恨,速度快
并發(fā)標記(和用戶線程一起):主要標記過程足删,標記全部對象
重新標記:由于并發(fā)標記時,用戶線程依然運行休里,因此在正式清理前,再做修正
并發(fā)清理(和用戶線程一起)基于標記結(jié)果,直接清理對象
- 清理不徹底
- 不能在空間快滿的時候清理
- -XX:CMSInitiatingOccupancyFraction設(shè)置觸發(fā)GC的閾值
-
如果不幸預留的空間不夠圾旨,就會引起concurrent mode failure
GC參數(shù)整理
類裝載器
class轉(zhuǎn)載驗證流程
加載
- 裝載類的第一個階段
- 取得類的二進制流
- 轉(zhuǎn)為方法區(qū)數(shù)據(jù)結(jié)構(gòu)
- 在java堆中生成對應的java.Lang.Class對象
鏈接
- 驗證:
目的是保證Class流的格式正確
文件格式驗證:
? ? ? ?是否以0xCAFEBABE開頭
? ? ? ?版本號是否合理
元數(shù)據(jù)驗證:
? ? ? ?是否有父類
? ? ? ?是否繼承了final類
? ? ? ?非抽象類實現(xiàn)了所有抽象方法
字節(jié)碼驗證(很復雜):
? ? ? ?運行檢查
? ? ? ?桟數(shù)據(jù)類型和操作碼數(shù)據(jù)是否吻合
? ? ? ?跳轉(zhuǎn)指令指定到合理的位置
符號引用驗證:
? ? ? ?常量池描述類是否存在
? ? ? ?訪問的方法或字段是否存在且足夠的權(quán)限 - 準備:
分配內(nèi)存,并為類設(shè)置初始值(方法區(qū)中)
? ? ? ?public static int v=1
? ? ? ?在準備階段魏蔗,v會被設(shè)置為0
? ? ? ?在初始化的<clinit>中才會被設(shè)置為1
? ? ? ?對于static final類型砍的,在準備階段就會被賦值 - 解析:
將符號引用替換為直接引用
符號引用:字符串,引用對象不一定被加載莺治。就是一個類的字符串表現(xiàn)廓鞠,比如說"java.lang.Object"
直接引用:指針或地址偏移量帚稠,引用對象一定在內(nèi)存
初始化
- 執(zhí)行類的構(gòu)造器<clinit>
static 變量 賦值語句
static{}語句 - 子類的<clinit>調(diào)用前保證父類的<clinit>被調(diào)用
- <clinit>是線程安全的
什么是ClassLoader
- ClassLoader是一個抽象類
- ClassLoader的實例將讀入java字節(jié)碼然后將類裝載到jvm中
- CLassLoader可以定制,滿足不懂的字節(jié)碼流獲取方式
- ClassLoader負責類裝載過程的加載階段
-
一些重要方法
-
ClassLoader的加載順序
自底向上查詢類是否已經(jīng)加載床佳,自頂向下嘗試加載類滋早。
我們寫的類一般都是在App ClassLoader中加載的,如果要查詢一個類是否已經(jīng)被加載夕土,先從App ClassLoader中查找馆衔,找不到再網(wǎng)上找。如果要加載一個類怨绣,先問一下Bootstrap ClassLoader有沒有角溃,沒有再往下走。
雙親模式的問題:
BootstrapLoader不能查找App ClassLoader中的類
解決方法:Thread.setContextClassLoader()
這是一個上下文加載器篮撑,是一個角色减细,用以頂層ClassLoader無法訪問底層ClassLoader的問題,基本思想是在頂層ClassLoader中傳入底層ClassLoader實例
java堆的分析
內(nèi)存溢出(OOM)的原因
- 在jvm中的內(nèi)存區(qū)間劃分
堆赢笨,永久區(qū)未蝌,線程桟,直接內(nèi)存 - 占用大量堆空間茧妒,直接溢出
- 永久區(qū)溢出萧吠,可以增大Perm區(qū)大小,運行Class回收
- java棧溢出
這里的桟指的是在創(chuàng)建線程的時候桐筏,需要為線程分配桟空間纸型,這個桟空間是向操作系統(tǒng)請求的,如果操作系統(tǒng)無法給出足夠的空間梅忌,就會拋出OOM狰腌。解決辦法:減少堆內(nèi)存,減少線程桟的大小
鎖
Mark Word牧氮,對象頭標記 32位
描述對象的hash琼腔,鎖信息,垃圾回收的年齡踱葛,標記
指向鎖記錄的指針
指向monitor的指針
GC標志
偏向鎖線程ID
偏向鎖
- 大部分情況下是沒有競爭的丹莲,所以可以通過偏向來提高性能
- 所謂的偏向,就是偏心尸诽,即鎖會偏向于當前已經(jīng)占有的鎖的線程
- 將對象投Mark的標記設(shè)置為偏向圾笨,并將線程ID寫入對象頭
- 只要沒有競爭,獲得偏向鎖的線程逊谋,在將來進入同步快擂达,不需要做同步
- 當其他線程請求相同的鎖時,偏向模式結(jié)束
- 在競爭激烈的場合,偏向鎖會增加系統(tǒng)負擔
輕量級鎖 BasicObjectLock
- 嵌入在線程桟中對象
包含兩個部分板鬓,一個是對象頭悲敷,另一個是指向持有這個鎖的對象的指針 - 普通的鎖處理性能不夠理想,輕量級鎖是一種快速鎖定的方法俭令。
- 如果對象沒有被鎖定
將對象頭的Mark指針保存到鎖對象中
將對象頭設(shè)置為指向鎖的指針(在線程桟空間中) - 如果輕量級鎖失敗后德,表示存在競爭,升級為重量級鎖(常規(guī)鎖)
- 在沒有競爭的前提下抄腔,減少傳統(tǒng)鎖使用OS互斥量產(chǎn)生的性能損耗
- 在競爭激烈時瓢湃,輕量級鎖會多做很多額外的操作,導致性能下降赫蛇。
自旋鎖
- 當競爭存在時绵患,如果線程可以很快獲得鎖,那么可以不在OS層掛起線程悟耘,讓線程做幾個空操作(自旋)
- 如果同步塊很長落蝙,自旋失敗,會降低系統(tǒng)性能
- 如果同步塊很短暂幼,自旋成功筏勒,節(jié)省線程掛起切換的時間,提升系統(tǒng)性能旺嬉。
獲取鎖的流程
首先嘗試獲取偏向鎖管行,如果可用,進入偏向模式邪媳,如果不可用捐顷,嘗試輕量級鎖,如果可用悲酷,使用輕量級鎖套菜,到此結(jié)束亲善,如果不可用设易,嘗試自旋鎖,如果成功蛹头,那就拿到鎖了顿肺,如果不成功,最后才會膨脹為重量鎖(普通鎖)渣蜗,在操作系統(tǒng)層面進行掛起屠尊。
代碼層面鎖的優(yōu)化
減少鎖的持有時間
盡量使用同步代碼塊而不是同步方法
減小鎖的粒度
- 將大對象,拆成小對象耕拷,大大增加并行度讼昆,降低鎖競爭
- 偏向鎖,輕量級鎖成功率更高
- ConcurrentHashMap
若干個Segment骚烧,一個Segment維護一個HashEntry浸赫,put操作時闰围,先定為到Segment,鎖定一個Segment既峡,執(zhí)行put
減小鎖粒度后羡榴,ConcurrentHashMap允許多個線程同時進入
鎖分離
- 根據(jù)功能分離
- ReadWriteLock
- 讀多寫少的情況,可以提高性能
鎖粗化
鎖消除
在即時編譯時运敢,如果發(fā)現(xiàn)不可能被共享的對象校仑,則可以消除這些所的操作
無鎖
- CAS(Compare And Swap)
- 非阻塞同步
- CAS(V,E,N)
- 應用層面判斷多線程的干擾,如果有干擾,則通知線程重試传惠。