1.java運(yùn)行時(shí)數(shù)據(jù)區(qū)
1.1程序計(jì)數(shù)器(線程獨(dú)有)
類(lèi)似于計(jì)算機(jī)中的寄存器,記錄當(dāng)前線程執(zhí)行的指令和行號(hào)楷拳。如我們所知舰罚,線程和進(jìn)行都是cpu執(zhí)行的時(shí)間片段榨馁,只是粒度大小不一樣而已。所有在線程進(jìn)行切換的過(guò)程中悼吱,需要保存線程進(jìn)入等待時(shí)的指令行號(hào)慎框,這樣下一次才能找到從哪個(gè)位置繼續(xù)執(zhí)行。
1.2 虛擬機(jī)棧(線程獨(dú)有)
當(dāng)一個(gè)線程的棧深度大于虛擬機(jī)所允許的深度的時(shí)候后添,將會(huì)拋出StackOverflowError異常笨枯;如果當(dāng)創(chuàng)建一個(gè)新的線程時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存,則會(huì)拋出OutOfMemeryError異常遇西。
1.2.1棧幀結(jié)構(gòu)
在執(zhí)行方法時(shí)馅精,會(huì)創(chuàng)建一個(gè)棧幀(一個(gè)方法對(duì)應(yīng)一個(gè)棧幀),并壓入虛擬機(jī)棧當(dāng)中粱檀。
1.2.2局部變量表和操作數(shù)棧
javap -c Test.class > p.txt 反匯編字節(jié)碼文件后洲敢,得到操作指令
上述方法執(zhí)行的指令含義如下:
本地變量表: this, i, j 。順序?yàn)?茄蚯,1压彭,2
1.加載本地變量表變量1的值,壓入操作數(shù)棧中渗常。
2.加載本地變量表變量2的值壮不,壓入操作數(shù)棧中。
3.壓入操作指令iadd皱碘,計(jì)算出結(jié)果并將之前的彈出棧外
4.返回int類(lèi)型的值
更多匯編指令參考:http://bbs.gupaoedu.com/forum.php?mod=viewthread&tid=295&highlight=javap
1.2.3動(dòng)態(tài)鏈接
多態(tài)情況下询一,方法動(dòng)態(tài)綁定時(shí),需要用到動(dòng)態(tài)鏈接
1.2.4方法出口
當(dāng)一個(gè)方法開(kāi)始執(zhí)行以后,只有兩種方法可以退出當(dāng)前方法:
當(dāng)執(zhí)行遇到返回指令健蕊,會(huì)將返回值傳遞給上層的方法調(diào)用者菱阵,這種退出的方式稱(chēng)為正常完成出口(Normal Method Invocation Completion),一般來(lái)說(shuō)缩功,調(diào)用者的PC計(jì)數(shù)器可以作為返回地址晴及。
當(dāng)執(zhí)行遇到異常,并且當(dāng)前方法體內(nèi)沒(méi)有得到處理掂之,就會(huì)導(dǎo)致方法退出抗俄,此時(shí)是沒(méi)有返回值的,稱(chēng)為異常完成出口(Abrupt Method Invocation Completion)世舰,返回地址要通過(guò)異常處理器表來(lái)確定。
當(dāng)方法返回時(shí)槽卫,可能進(jìn)行3個(gè)操作:
恢復(fù)上層方法的局部變量表和操作數(shù)棧
把返回值壓入調(diào)用者調(diào)用者棧幀的操作數(shù)棧
調(diào)整 PC 計(jì)數(shù)器的值以指向方法調(diào)用指令后面的一條指令
1.3本地方法棧(線程獨(dú)有)
調(diào)用本地方法時(shí)使用
1.4方法區(qū)
1.5heap
2.java內(nèi)存模型(JMM, java memory model)
2.1內(nèi)存結(jié)構(gòu)
問(wèn)題1:新生代的垃圾回收算法為什么用復(fù)制算法跟压?
因?yàn)樾律膶?duì)象90%都是朝生夕死,用標(biāo)記清除法容易造成內(nèi)存碎片歼培,而標(biāo)記整理法代價(jià)太高
所以采用復(fù)制算法震蒋。
問(wèn)題2:為什么新生代不直接設(shè)置兩個(gè)區(qū),比例為1:1躲庄,或者9:1
如果是1:1的查剖,空間利用率只有50%。如果是9:1的話噪窘,大部分對(duì)象的分代年齡只能達(dá)到1笋庄,也就是經(jīng)過(guò)一次minorGC就會(huì)進(jìn)入到老年代中。
問(wèn)題3:怎么控制survivor的比例和新老年代的內(nèi)存比例倔监?
-XX:SurvivorRatio=8
-XX:NewRatio=2
更多JVM參數(shù)請(qǐng)參考:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
2.2對(duì)象生命周期
2.2.1類(lèi)加載檢查
在對(duì)象創(chuàng)建之前直砂,需要先判斷類(lèi)是否已經(jīng)被加載,判斷方式在當(dāng)前類(lèi)加載器中尋找該類(lèi)的類(lèi)信息浩习,如果沒(méi)有找到静暂,則是未加載,因此多個(gè)類(lèi)加載器可以加載同一個(gè)類(lèi)谱秽。
2.2.2為對(duì)象分配內(nèi)存
問(wèn)題1:如何知道哪塊是可用內(nèi)存洽蛀?
如果采用的垃圾收集器是Serial、ParNew等帶Compact過(guò)程的收集器的時(shí)候(新生代創(chuàng)建對(duì)象疟赊,復(fù)制對(duì)象)郊供,內(nèi)存是規(guī)整的,采用移動(dòng)指針的方式進(jìn)行分配听绳。
如果采用的垃圾回收器是CMS這種采用標(biāo)記清除法的時(shí)候(新生代對(duì)象移動(dòng)到老年代)颂碘,內(nèi)存是不規(guī)整的,采用空閑列表的方式記錄可用的內(nèi)存區(qū)域。
問(wèn)題2:多個(gè)線程同時(shí)創(chuàng)建對(duì)象的話头岔,會(huì)出現(xiàn)并發(fā)問(wèn)題塔拳,如何解決?
- 指針碰撞 (使用CAS來(lái)同步)
- TLAB thread local allocation buffer峡竣,為每個(gè)線程分配一個(gè)緩沖區(qū)靠抑。-XX:+UseTLAB打開(kāi)此策略。
2.3初始化內(nèi)存空間
內(nèi)存分配完成之后适掰,虛擬機(jī)會(huì)將分配空間內(nèi)都初始化為零值(不包括對(duì)象頭)颂碧,如果使用TLAB分配,這一過(guò)程也可以提前至TLAB分配時(shí)進(jìn)行类浪。
2.4設(shè)置對(duì)象頭
包括對(duì)象的哈希碼载城、類(lèi)元素信息、GC分代年齡等费就。這些信息都放置在對(duì)象頭中诉瓦。
2.5執(zhí)行<init>方法
2.6對(duì)象回收
問(wèn)題1:什么樣的對(duì)象需要被回收?
無(wú)用的對(duì)象力细,即外部引用指向的對(duì)象睬澡。
問(wèn)題2:怎么判斷該對(duì)象已無(wú)引用?
- 引用計(jì)數(shù)法眠蚂,無(wú)法解決循環(huán)引用的問(wèn)題
- 可達(dá)性分析 GCroot不可達(dá)
問(wèn)題3:什么對(duì)象可以成為GCRoot?
局部變量表中引用的對(duì)象煞聪,靜態(tài)變量引用的對(duì)象,常量引用逝慧。
問(wèn)題4:強(qiáng)引用昔脯,軟引用,弱引用馋艺,虛引用的對(duì)象回收策略栅干。
- 強(qiáng)引用
Object obj = new Object(); // 這個(gè)obj就是對(duì)象的強(qiáng)引用,只要有強(qiáng)引用存在捐祠,就不會(huì)被回收碱鳞。
- 軟引用
Object obj = new Object(); // obj是強(qiáng)引用
SoftReference<Object> sf = new SoftReference<Object>(obj); // sf是軟引用
obj = null; // 強(qiáng)引用置空
sf.get();//有時(shí)候會(huì)返回null //垃圾回收時(shí),如果內(nèi)存空間不足踱蛀,會(huì)回收軟引用的對(duì)象窿给。
- 弱引用
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有時(shí)候會(huì)返回null
wf.isEnQueued();//弱應(yīng)用對(duì)象只能存活到下一次垃圾回收
- 虛引用
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永遠(yuǎn)返回null
pf.isEnQueued();//返回是否從內(nèi)存中已經(jīng)刪除
虛引用是每次垃圾回收的時(shí)候都會(huì)被回收,通過(guò)虛引用的get方法永遠(yuǎn)獲取到的數(shù)據(jù)為null率拒,因此也被成為幽靈引用崩泡。
虛引用主要用于檢測(cè)對(duì)象是否已經(jīng)從內(nèi)存中刪除
各類(lèi)引用使用場(chǎng)景請(qǐng)參考:https://www.cnblogs.com/yw-ah/p/5830458.html
2.7 對(duì)象死亡拯救
3.垃圾回收
3.1垃圾回收算法
-
標(biāo)記清除算法
最基礎(chǔ)的算法便是標(biāo)記-清除算法(Mark-Sweep)。算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記處需要收集的對(duì)象猬膨,在標(biāo)記完成之后角撞,再統(tǒng)一回收所有被標(biāo)記的對(duì)象。
這是最簡(jiǎn)單的一種算法,但是缺點(diǎn)也是很明顯的:一個(gè)是效率問(wèn)題谒所,標(biāo)記和清除效率都不高热康。二是空間問(wèn)題,清除之后會(huì)產(chǎn)生大量的空間碎片劣领,導(dǎo)致之后分配大對(duì)象找不到足夠的連續(xù)對(duì)象而不得不觸發(fā)另一次垃圾收集動(dòng)作姐军。
標(biāo)記清除算法 -
復(fù)制算法
復(fù)制算法(Copying)將可用內(nèi)存按照容量大小分成相等的兩份,每次只使用一半尖淘。當(dāng)這一塊內(nèi)存用完了奕锌,就會(huì)將還存活的對(duì)象復(fù)制到另一塊內(nèi)存上,然后將之前的那塊內(nèi)存清空村生。
優(yōu)點(diǎn)是解決了空間碎片的問(wèn)題惊暴,而且分配新對(duì)象的時(shí)候順序分配,實(shí)現(xiàn)簡(jiǎn)單梆造,運(yùn)行高效缴守。缺點(diǎn)是內(nèi)存減小了一半。
復(fù)制算法 -
標(biāo)記整理法
復(fù)制算法在對(duì)象存活率較高的情況下镇辉,效率會(huì)變低。而且浪費(fèi)了50%的空間贴捡。
根據(jù)老年代的特點(diǎn)忽肛,有人提出了另外一種“標(biāo)記-整理”算法(Mark-Compact)。算法的也分為標(biāo)記和整理兩個(gè)階段烂斋。標(biāo)記和“標(biāo)記-清除”算法的標(biāo)記過(guò)程一樣屹逛。當(dāng)標(biāo)記完成之后,并不直接對(duì)可回收對(duì)象進(jìn)行整理汛骂,而是所有存活的對(duì)象整理成連續(xù)的罕模,然后清理掉剩余的空間。
標(biāo)記整理法
3.2 分代垃圾回收和垃圾收集器
- youngGC
當(dāng)eden區(qū)空間不足時(shí)觸發(fā),采用回收算法是復(fù)制帘瞭。在youngGC之前淑掌,會(huì)進(jìn)行一次分配擔(dān)保。
進(jìn)行youngGC時(shí)蝶念,會(huì)在from和to區(qū)來(lái)回復(fù)制抛腕,每次youngGC,對(duì)象的年齡就會(huì)加1。當(dāng)新生代空間不足時(shí)媒殉,或?qū)ο筮_(dá)到一定年齡后會(huì)進(jìn)入老年代担敌。
問(wèn)題1:分配擔(dān)保機(jī)制:
在發(fā)生Minor GC之前,虛擬機(jī)會(huì)先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間廷蓉,如果這個(gè)條件成立全封,那么Minor GC可以確保是安全的。如果不成立,則虛擬機(jī)會(huì)查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗刹悴。如果允許行楞,那么會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小,如果大于颂跨,將嘗試著進(jìn)行一次Minor GC敢伸,盡管這次Minor GC是有風(fēng)險(xiǎn)的;如果小于恒削,或者HandlePromotionFailure設(shè)置不允許冒險(xiǎn)池颈,那這時(shí)也要改為進(jìn)行一次Full GC。
問(wèn)題2:什么對(duì)象會(huì)進(jìn)入老年代钓丰?
對(duì)象很大 -XX:PretenureSizeThreshold=3145728 3M
長(zhǎng)期存活的對(duì)象 -XX:MaxTenuringThreshold=15
動(dòng)態(tài)年齡判斷 相同年齡所有對(duì)象的大小總和 > Suvivor空間的一般躯砰,將會(huì)重新設(shè)置晉升年齡閾值 - oldGC
老年代回收發(fā)生在剩余內(nèi)存無(wú)法裝載新生代存活的對(duì)象的時(shí)候和無(wú)法裝載大對(duì)象的時(shí)候(大對(duì)象直接進(jìn)入老年代。CMS的回收算法是標(biāo)記清除法携丁,其余如Serial Old, Parallel為標(biāo)記整理算法琢歇。 - fullGC
觸發(fā)機(jī)制:
① 永久代空間不足
② 分配擔(dān)保檢查不符合minorGC的條件
回收整個(gè)heap和metaspace(1.8之前叫做永久代),此過(guò)程十分緩慢
垃圾收集器
- Serial
特點(diǎn):?jiǎn)尉€程梦鉴,會(huì)造成用戶(hù)停頓李茫。
VM參數(shù):
-XX:+UserSerialGC 在新生代和老年代使用串行收集器
-XX:+SurvivorRatio 設(shè)置eden和survivor區(qū)大小的比例
-XX:+PretenureSizeThreshold 直接晉升到年老代的對(duì)象大小,設(shè)置此參數(shù)后肥橙,超過(guò)該大小的對(duì)象直接在年老代中分配內(nèi)存
-XX:+MaxTenuringThreshold 直接晉升到年老代的對(duì)象年齡魄宏,每個(gè)對(duì)象在一次Minor GC之后還存活,則年齡加1存筏,當(dāng)年齡超過(guò)該值時(shí)進(jìn)入年老代 - ParNew
特點(diǎn):serial的多線程版本
VM參數(shù):
-XX:+UseParNewGC - Parallel Scavenge
特點(diǎn):關(guān)注吞吐量宠互,而不是用戶(hù)停頓
VM參數(shù):
-XX:+UseParNewGC 打開(kāi)此開(kāi)關(guān)參數(shù)后,使用ParNew+Serial Old收集器組合進(jìn)行垃圾收集椭坚。
-XX:+UseParallelOldGC 打開(kāi)此開(kāi)關(guān)參數(shù)后予跌,使用Parallel Scavenge+Parallel Old收集器組合進(jìn)行垃圾收集。
-XX:+ParallelGCThreads 設(shè)置并行GC時(shí)進(jìn)行內(nèi)存回收的線程數(shù)善茎。
-XX:+MaxGCPauseMillis Parallel Scavenge收集器最大GC停頓時(shí)間券册。
-XX:+GCTimeRation Parallel Scavenge收集器運(yùn)行時(shí)間占總時(shí)間比率。
-XX:+UseAdaptiveSizePolicy java虛擬機(jī)動(dòng)態(tài)自適應(yīng)策略巾表,動(dòng)態(tài)調(diào)整年老代對(duì)象年齡和各個(gè)區(qū)域大小汁掠。 - Serial Old
特點(diǎn):標(biāo)記整理算法,單線程集币,CMS備用方案 - Parallel Old
特點(diǎn):多線程考阱,標(biāo)記整理 -
CMS
特點(diǎn): 多線程,標(biāo)記清除鞠苟,容易出發(fā)FullGC
CMS執(zhí)行過(guò)程:
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器乞榨。主要用于互聯(lián)網(wǎng)或B/S系統(tǒng)的服務(wù)端秽之,這類(lèi)應(yīng)用尤其重視服務(wù)的響應(yīng)速度。
從名字可以看出吃既,CMS是基于“標(biāo)記-清除”算法的考榨,運(yùn)作過(guò)程更加復(fù)雜一些,分為4個(gè)步驟:
①.初始標(biāo)記(CMS initial mark) 標(biāo)記GC Roots直接關(guān)聯(lián)的對(duì)象
②.并發(fā)標(biāo)記(CMS concurrenr mark) 可達(dá)性分析算法
③.重新標(biāo)記(CMS remark) 并發(fā)變動(dòng)修改
④.并發(fā)清除(CMS concurrent sweep)
其中初始標(biāo)記鹦倚、重新標(biāo)記這兩個(gè)步驟任然需要停頓其他用戶(hù)線程河质。初始標(biāo)記僅僅只是標(biāo)記出GC ROOTS能直接關(guān)聯(lián)到的對(duì)象,速度很快震叙,并發(fā)標(biāo)記階段是進(jìn)行GC ROOTS 根搜索算法階段掀鹅,會(huì)判定對(duì)象是否存活。而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間媒楼,因用戶(hù)程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄乐尊,這個(gè)階段的停頓時(shí)間會(huì)比初始標(biāo)記階段稍長(zhǎng),但比并發(fā)標(biāo)記階段要短划址。由于整個(gè)過(guò)程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過(guò)程中扔嵌,收集器線程都可以與用戶(hù)線程一起工作,所以整體來(lái)說(shuō)夺颤,CMS收集器的內(nèi)存回收過(guò)程是與用戶(hù)線程一起并發(fā)執(zhí)行的痢缎。執(zhí)行過(guò)程如下圖。
CMS
CMS收集器的優(yōu)點(diǎn):并發(fā)收集世澜、低停頓
CMS收集器的缺點(diǎn):
①CPU資源非常敏感牺弄。在并發(fā)階段,雖然不會(huì)導(dǎo)致用戶(hù)線程停頓宜狐,但是會(huì)占用CPU資源而導(dǎo)致引用程序變慢,總吞吐量下降蛇捌。
②由于CMS并發(fā)清理階段用戶(hù)線程還在運(yùn)行抚恒,伴隨程序的運(yùn)行自熱會(huì)有新的垃圾不斷產(chǎn)生,這一部分垃圾出現(xiàn)在標(biāo)記過(guò)程之后络拌,CMS無(wú)法在本次收集中處理它們俭驮,只好留待下一次GC時(shí)將其清理掉。這一部分垃圾稱(chēng)為“浮動(dòng)垃圾”春贸。也是由于在垃圾收集階段用戶(hù)線程還需要運(yùn)行混萝,即需要預(yù)留足夠的內(nèi)存空間給用戶(hù)線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿(mǎn)了再進(jìn)行收集萍恕,需要預(yù)留一部分內(nèi)存空間提供并發(fā)收集時(shí)的程序運(yùn)作使用逸嘀。在默認(rèn)設(shè)置下,CMS收集器在老年代使用了68%的空間時(shí)就會(huì)被激活允粤,也可以通過(guò)參數(shù)-XX:CMSInitiatingOccupancyFraction的值來(lái)提供觸發(fā)百分比崭倘,以降低內(nèi)存回收次數(shù)提高性能翼岁。要是CMS運(yùn)行期間預(yù)留的內(nèi)存無(wú)法滿(mǎn)足程序其他線程需要,就會(huì)出現(xiàn)“Concurrent Mode Failure”失敗司光,這時(shí)候虛擬機(jī)將啟動(dòng)后備預(yù)案:臨時(shí)啟用Serial Old收集器來(lái)重新進(jìn)行老年代的垃圾收集琅坡,這樣停頓時(shí)間就很長(zhǎng)了。所以說(shuō)參數(shù)-XX:CMSInitiatingOccupancyFraction設(shè)置的過(guò)高將會(huì)很容易導(dǎo)致“Concurrent Mode Failure”失敗残家,性能反而降低榆俺。
③CMS是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的收集器,使用“標(biāo)記-清除”算法收集后坞淮,會(huì)產(chǎn)生大量碎片茴晋。空間碎片太多時(shí)碾盐,將會(huì)給對(duì)象分配帶來(lái)很多麻煩晃跺,比如說(shuō)大對(duì)象,內(nèi)存空間找不到連續(xù)的空間來(lái)分配不得不提前觸發(fā)一次Full GC毫玖。為了解決這個(gè)問(wèn)題掀虎,CMS收集器提供了一個(gè)-XX:UseCMSCompactAtFullCollection開(kāi)關(guān)參數(shù),用于在Full GC之后增加一個(gè)碎片整理過(guò)程付枫,還可通過(guò)-XX:CMSFullGCBeforeCompaction參數(shù)設(shè)置執(zhí)行多少次不壓縮的Full GC之后烹玉,跟著來(lái)一次碎片整理過(guò)程。 - G1
特點(diǎn):內(nèi)存布局改變阐滩,它將整個(gè)Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region)二打,雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了掂榔,它們都是一部分Region(不需要連續(xù))的集合继效。
G1收集器的運(yùn)作大致可劃分為以下幾個(gè)步驟:
初始標(biāo)記(Initial Marking)
并發(fā)標(biāo)記(Concurrent Marking)
最終標(biāo)記(Final Marking)
篩選回收(Live Data Counting and Evacuation)
3.java監(jiān)控
3.1 查看GC類(lèi)型和GC日志
-
查看GC類(lèi)型
-XX:+PrintFlagsFinal -XX:+PrintCommandLineFlags
GC收集器默認(rèn)類(lèi)型是 Parallel Scanvage + Parallel Old
-
查看GC日志
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/home/administrator/james/gc.log-XX:+PrintHeapAtGC
3.2 jmap
查看內(nèi)存分配情況以及設(shè)置
3.3 jstat
查看內(nèi)存使用情況以及gc次數(shù)
查看更多信息請(qǐng)參考:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jstat.html
3.4jstack (thread dump)
jstack 4170
3.5 jvisualvm & jsoncole
4.常見(jiàn)問(wèn)題排查
4.1cpu占用過(guò)高
https://jingyan.baidu.com/article/4f34706e3ec075e387b56df2.html
4.2 內(nèi)存泄漏排查
GC回收不掉