這是傳說中程序員的一天:
8:00 被第N遍虐心的鬧鈴從床上拉下棺耍,木然洗漱,泡沫掛嘴角副砍;
9:00 插在地鐵的沙丁魚罐頭里,和著韭菜香和汗臭,盲目的翻著朋友圈咒锻、公眾號(hào)和技術(shù)論壇;
10:00 公司守屉,嚼著油條惑艇,等電腦收完最后一封郵件,開始重復(fù)的日常工作胸梆,敲敲敲敲敲代碼
12:00 外賣小哥的到來敦捧,宣布午飯娛樂時(shí)間,邊吃雞邊吃雞碰镜,然后窩個(gè)覺
14:00 睜眼兢卵,繼續(xù),回回回回回郵件绪颖,重復(fù)敲代碼
16:00 被PM叫去撕撕撕撕撕撕需求秽荤、接著改代碼
18:00 被leader叫去,催催催催催進(jìn)度
19:00 被肚子提醒柠横,訂餐吃飯
21:00 擼擼擼擼擼完最后一行代碼后窃款,點(diǎn)上一支煙,等著bug來襲
23:00 到家牍氛,繼續(xù)解bug到深夜夜夜夜夜夜夜晨继,然后洗洗睡吧
看到這段文字有多少人感同身受?我中招了搬俊,渾渾噩噩的往復(fù)紊扬,默默看著理想老去蜒茄,難道這是當(dāng)初走上“攻城獅”道路時(shí)想要的生活?
行業(yè)內(nèi)競(jìng)爭(zhēng)越來越激烈餐屎,我們先來看看招聘需求的結(jié)構(gòu)性變化:
Java的招聘總量在減少檀葛,但中高端職位數(shù)量從未減少,甚至還有增加的趨勢(shì)腹缩,這說明行業(yè)變得成熟起來屿聋,但對(duì)我們Java同學(xué)們來講,卻不得不面對(duì)越來越嚴(yán)峻的挑戰(zhàn):不進(jìn)則退的危機(jī)越發(fā)明顯藏鹊。
看看我們都遇到了什么:
好不容易得到了阿里P6/P7的面試機(jī)會(huì)润讥,當(dāng)被面試官問到JVM相關(guān)問題時(shí),好看的面試官同學(xué)一張嘴:在討論技術(shù)方案時(shí):
看到這有多少人感同身受伙判?我中招了象对,渾渾噩噩的往復(fù),默默看著理想老去宴抚,難道這是當(dāng)初走上“攻城獅”道路時(shí)想要的生活勒魔?
接下來,讓我跟大家一起-深入理解JVM
一菇曲、JVM簡(jiǎn)介
JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫冠绢,JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來的計(jì)算機(jī)常潮,是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的弟胀。
Java語言的一個(gè)非常重要的特點(diǎn)就是跨平臺(tái)性。而使用Java虛擬機(jī)是實(shí)現(xiàn)這一特點(diǎn)的關(guān)鍵喊式。一般的高級(jí)語言如果要在不同的平臺(tái)上運(yùn)行孵户,至少需要編譯成不同的目標(biāo)代碼。而引入Java語言虛擬機(jī)后岔留,Java語言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯夏哭。Java語言使用Java虛擬機(jī)屏蔽了與具體平臺(tái)相關(guān)的信息,使得Java語言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼)献联,就可以在多種平臺(tái)上不加修改地運(yùn)行竖配。Java虛擬機(jī)在執(zhí)行字節(jié)碼時(shí),把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行里逆。這就是Java的能夠“一次編譯进胯,到處運(yùn)行”的原因。
二原押、JVM內(nèi)存管理模型
JVM內(nèi)存結(jié)構(gòu)主要又三大塊:堆內(nèi)存胁镐、方法區(qū)和棧。
1、堆內(nèi)存(Heap)
對(duì)于大多數(shù)應(yīng)用來說希停,Java堆(Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊烁巫。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建宠能。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存磁餐。
Java堆內(nèi)存中還可以細(xì)分為:年輕代和老年代违崇。其中,年輕代又可以分為Eden區(qū)诊霹、From Survivor空間羞延、To Survivor空間等。
如果在堆中沒有內(nèi)存完成實(shí)例分配脾还,并且堆也無法再擴(kuò)展時(shí)伴箩,將會(huì)拋出OutOfMemoryError異常。
2鄙漏、方法區(qū)(Method Area)
方法區(qū)(Method Area)與Java堆一樣嗤谚,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息怔蚌、常量巩步、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)桦踊。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分椅野,但是它卻有一個(gè)別名叫做Non-Heap(非堆),目的應(yīng)該是與Java堆區(qū)分開來籍胯。
對(duì)于習(xí)慣在HotSpot虛擬機(jī)上開發(fā)和部署程序的開發(fā)者來說竟闪,很多人愿意把方法區(qū)稱為“永久代”(Permanent Generation),本質(zhì)上兩者并不等價(jià)杖狼,僅僅是因?yàn)镠otSpot虛擬機(jī)的設(shè)計(jì)團(tuán)隊(duì)選擇把GC分代收集擴(kuò)展至方法區(qū)炼蛤,或者說使用永久代來實(shí)現(xiàn)方法區(qū)而已。
Java虛擬機(jī)規(guī)范對(duì)這個(gè)區(qū)域的限制非常寬松本刽,除了和Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外鲸湃,還可以選擇不實(shí)現(xiàn)垃圾收集。相對(duì)而言子寓,垃圾收集行為在這個(gè)區(qū)域是比較少出現(xiàn)的暗挑,但并非數(shù)據(jù)進(jìn)入了方法區(qū)就如永久代的名字一樣“永久”存在了。這個(gè)區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類型的卸載斜友,一般來說這個(gè)區(qū)域的回收“成績(jī)”比較難以令人滿意炸裆,尤其是類型的卸載,條件相當(dāng)苛刻鲜屏,但是這部分區(qū)域的回收確實(shí)是有必要的烹看。
根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定国拇,當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí),將拋出OutOfMemoryError異常惯殊。
3酱吝、程序計(jì)數(shù)器(Program Counter Register)
程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器土思。在虛擬機(jī)的概念模型里(僅是概念模型务热,各種虛擬機(jī)可能會(huì)通過一些更高效的方式去實(shí)現(xiàn)),字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令己儒,分支崎岂、循環(huán)、跳轉(zhuǎn)闪湾、異常處理冲甘、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成。
由于Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的途样,在任何一個(gè)確定的時(shí)刻江醇,一個(gè)處理器(對(duì)于多核處理器來說是一個(gè)內(nèi)核)只會(huì)執(zhí)行一條線程中的指令。因此娘纷,為了線程切換后能恢復(fù)到正確的執(zhí)行位置嫁审,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線程之間的計(jì)數(shù)器互不影響赖晶,獨(dú)立存儲(chǔ)律适,我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存。
如果線程正在執(zhí)行的是一個(gè)Java方法遏插,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址捂贿;如果正在執(zhí)行的是Natvie方法,這個(gè)計(jì)數(shù)器值則為空胳嘲。
此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域厂僧。
4、JVM棧(JVM Stacks)
與程序計(jì)數(shù)器一樣了牛,Java虛擬機(jī)棧(Java Virtual Machine Stacks)也是線程私有的颜屠,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行的時(shí)候都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表鹰祸、操作棧甫窟、動(dòng)態(tài)鏈接、方法出口等信息蛙婴。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過程粗井,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。
局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean、byte浇衬、char懒构、short、int耘擂、float胆剧、long、double)梳星、對(duì)象引用(reference類型赞赖,它不等同于對(duì)象本身,根據(jù)不同的虛擬機(jī)實(shí)現(xiàn)冤灾,它可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔槪部赡苤赶蛞粋€(gè)代表對(duì)象的句柄或者其他與此對(duì)象相關(guān)的位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)辕近。
其中64位長(zhǎng)度的long和double類型的數(shù)據(jù)會(huì)占用2個(gè)局部變量空間(Slot)韵吨,其余的數(shù)據(jù)類型只占用1個(gè)。局部變量表所需的內(nèi)存空間在編譯期間完成分配移宅,當(dāng)進(jìn)入一個(gè)方法時(shí)归粉,這個(gè)方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運(yùn)行期間不會(huì)改變局部變量表的大小漏峰。
在Java虛擬機(jī)規(guī)范中糠悼,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常狀況:如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError異常浅乔。如果虛擬機(jī)椌笪梗可以動(dòng)態(tài)擴(kuò)展(當(dāng)前大部分的Java虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展,只不過Java虛擬機(jī)規(guī)范中也允許固定長(zhǎng)度的虛擬機(jī)棧)靖苇,當(dāng)擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存時(shí)會(huì)拋出OutOfMemoryError異常席噩。
5、本地方法棧(Native Method Stacks)
本地方法棧(Native Method Stacks)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的贤壁,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)悼枢,而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。虛擬機(jī)規(guī)范中對(duì)本地方法棧中的方法使用的語言脾拆、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定馒索,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它。甚至有的虛擬機(jī)(譬如Sun HotSpot虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一名船。與虛擬機(jī)棧一樣绰上,本地方法棧區(qū)域也會(huì)拋出StackOverflowError和OutOfMemoryError異常。
三包帚、JVM內(nèi)存分配機(jī)制JVM內(nèi)存≈Heap(堆內(nèi)存)+PermGen(方法區(qū))+Thrend(棧)
Heap(堆內(nèi)存)=Young(年輕代)+Old(老年代)渔期,官方文檔建議整個(gè)年輕代占整個(gè)堆內(nèi)存的3/8,老年代占整個(gè)堆內(nèi)存的5/8,但是可以配置為其他比例疯趟。
Young(年輕代)=EdenSpace+FromSurvivor+ToSurvivor拘哨,Eden區(qū)與兩個(gè)存活區(qū)的內(nèi)存大小比例是:8:1:1,同樣可以配置為其他比例信峻。
四倦青、JVM垃圾回收機(jī)制1、new出來的對(duì)象先放在Eden區(qū)盹舞,Eden區(qū)放滿后第一次觸發(fā)Young GC(垃圾回收)产镐,把存活對(duì)象移到S1存活區(qū)。
2踢步、第二次Eden區(qū)又滿了癣亚,再次觸發(fā)Young GC,把Eden區(qū)的存活對(duì)象移到S1存活區(qū)获印,把S0存活區(qū)的存活對(duì)象也移到S2存活區(qū)述雾,這時(shí)S1存活區(qū)清空了。
3兼丰、第三次Eden區(qū)又滿了玻孟,再次觸發(fā)Young GC,把Eden區(qū)的存活對(duì)象移到S0存活區(qū)鳍征,把S1存活區(qū)的存活對(duì)象也移到S1存活區(qū)黍翎,這時(shí)S2存活區(qū)清空了。
4艳丛、這樣S0和S1交替互換匣掸,輪流為清空,大大拉長(zhǎng)了存活對(duì)象進(jìn)入老年代的時(shí)間間隔质礼。
類對(duì)象什么時(shí)候進(jìn)入老年代:
a旺聚、大對(duì)象直接進(jìn)入老年代:Eden區(qū)放不下直接進(jìn)入老年代
b、長(zhǎng)期存活的對(duì)象進(jìn)入老年代:以Young GC次數(shù)進(jìn)行判斷的眶蕉,默認(rèn)次數(shù)15次后進(jìn)入老年代
c砰粹、執(zhí)行Young GC時(shí),存活區(qū)放不下時(shí)造挽,存活對(duì)象也直接進(jìn)入老年代
5碱璃、一直這樣循環(huán)往復(fù)直到老年代滿了,觸發(fā)Full GC饭入。首先清除老年代中的沒有引用的對(duì)象嵌器,再對(duì)Eden區(qū)進(jìn)行GC,還會(huì)對(duì)持久代進(jìn)行GC(持久代一般沒什么可清理)
6谐丢、老年代里面放滿以后爽航,執(zhí)行Full GC也釋放不了內(nèi)存空間蚓让,就會(huì)報(bào)內(nèi)存溢出的錯(cuò)誤了。
總結(jié):
1讥珍、Young GC只發(fā)生在Eden區(qū)历极,Eden區(qū)是整個(gè)Java堆內(nèi)存分配的入口,new對(duì)象優(yōu)先分配到Eden區(qū)衷佃,Eden區(qū)滿之后觸發(fā)Young GC
2趟卸、Young GC觸發(fā)后,然后它會(huì)判斷Eden區(qū)的對(duì)象是否是存活的氏义,如果是存活的則放到存活區(qū)锄列,不是存活的則清除掉釋放內(nèi)存空間。
3惯悠、觸發(fā)Full GC是雖然也清理了Eden區(qū)邻邮,但是Young GC次數(shù)不會(huì)+1,它是Full GC在干活克婶。
什么時(shí)候觸發(fā)Full GC:
a饶囚、老年代空間不足
b、持久代空間不足的時(shí)候也會(huì)觸發(fā)Full GC
c鸠补、顯示調(diào)用也可以觸發(fā)Full GC,比如說RunTime.GC嘀掸、System.GC
d紫岩、RMI框架,會(huì)產(chǎn)生大量的對(duì)象睬塌,會(huì)進(jìn)行顯示調(diào)用泉蝌,觸發(fā)Full GC
e、Young GC時(shí)的悲觀策略dump live的內(nèi)存信息時(shí)(jmap-dump:live)
4揩晴、執(zhí)行Young GC和Full GC應(yīng)用程序的所有線程都是暫停的勋陪、停止工作,但Full GC時(shí)間比較長(zhǎng)
5硫兰、JVM調(diào)優(yōu)的核心思想:
a诅愚、盡量減少Full GC的次數(shù),或者說延長(zhǎng)Full GC間隔時(shí)間劫映。不要頻繁觸發(fā)Full GC违孝,因?yàn)閳?zhí)行Full GC的時(shí)間比較長(zhǎng)。
b泳赋、盡量減少Young GC執(zhí)行的時(shí)間
五雌桑、JVM內(nèi)存調(diào)優(yōu)思路
首先需要注意的是在對(duì)JVM內(nèi)存調(diào)優(yōu)的時(shí)候不能只看操作系統(tǒng)級(jí)別Java進(jìn)程所占用的內(nèi)存,這個(gè)數(shù)值不能準(zhǔn)確的反應(yīng)堆內(nèi)存的真實(shí)占用情況祖今,因?yàn)镚C過后這個(gè)值是不會(huì)變化的校坑,因此內(nèi)存調(diào)優(yōu)的時(shí)候要更多地使用JDK提供的內(nèi)存查看工具拣技,比如JConsole和Java VisualVM。
對(duì)JVM內(nèi)存的系統(tǒng)級(jí)的調(diào)優(yōu)主要的目的是減少Young GC的頻率和Full GC的次數(shù)耍目,過多的GC和Full GC是會(huì)占用很多的系統(tǒng)資源(主要是CPU)膏斤,影響系統(tǒng)的吞吐量。特別要關(guān)注Full GC制妄,因?yàn)樗鼤?huì)對(duì)整個(gè)堆內(nèi)存進(jìn)行整理掸绞,導(dǎo)致Full GC一般由于以下幾種情況:
1、老年代空間不足
調(diào)優(yōu)時(shí)盡量讓對(duì)象在年輕代Young GC時(shí)被回收耕捞、讓對(duì)象在年輕代多存活一段時(shí)間和不要?jiǎng)?chuàng)建過大的對(duì)象及數(shù)組避免直接在老年代創(chuàng)建對(duì)象
2衔掸、Pemanet Generation空間不足
增大Perm Gen空間,避免太多靜態(tài)對(duì)象
3俺抽、System.gc()被顯示調(diào)用
垃圾回收不要手動(dòng)觸發(fā)敞映,盡量依靠JVM自身的機(jī)制,調(diào)優(yōu)手段主要是通過控制堆內(nèi)存的各個(gè)部分的比例和GC策略來實(shí)現(xiàn)磷斧。
下面來看看各部分比例不良設(shè)置會(huì)導(dǎo)致什么后果
1)年輕代設(shè)置過小
一是年輕代Young GC次數(shù)非常頻繁振愿,增大系統(tǒng)消耗
二是導(dǎo)致大對(duì)象直接進(jìn)入老年代,占據(jù)了老年代剩余空間弛饭,誘發(fā)Full GC
2)年輕代設(shè)置過大
一是年輕代設(shè)置過大會(huì)導(dǎo)致老年代過忻崮(堆總量一定),從而誘發(fā)Full GC
二是年輕代Young GC耗時(shí)大幅度增加
一般說來年輕代占整個(gè)堆內(nèi)存的1/3比較合適
3)Survivor設(shè)置過小
導(dǎo)致對(duì)象從Eden區(qū)直接到達(dá)老年代侣颂,降低了在年輕代的存活時(shí)間
4)Survivor設(shè)置過大
導(dǎo)致Eden區(qū)過小档桃,增加了Young GC頻率
另外,通過-XX:MaxTenuringThreshold=n來控制年輕代存活時(shí)間憔晒,盡量讓對(duì)象在年輕代被回收
六藻肄、垃圾回收策略
1、串行收集
串行收集使用單線程處理所有垃圾回收工作拒担,因?yàn)闊o需多線程交互嘹屯,而且效率比較高。但是从撼,也無法使用多處理器的優(yōu)勢(shì)州弟,所以串行收集適合單核處理器機(jī)器。當(dāng)然谋逻,此收集器也可以用在小數(shù)據(jù)量(100M左右)情況下的多核處理器機(jī)器上呆馁,可以使用-XX:+UseSerialGC打開。
2毁兆、并行收集
并行收集浙滤,對(duì)年輕代進(jìn)行并行垃圾回收,因此可以減少垃圾回收時(shí)間气堕。一般在多線程多處理器機(jī)器上使用纺腊,使用-XX:+UseParallelGC.打開畔咧。并行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中迕行了增強(qiáng)--可以對(duì)年老代進(jìn)行并行收集揖膜。如果年老代不使用并發(fā)收集的話誓沸,默認(rèn)是使用單線程進(jìn)行垃圾回收,因此會(huì)制約擴(kuò)展能力壹粟,老年代開啟并發(fā)收集拜隧,使用-XX:+UseParallelOldGC打開。
使用-XX:ParallelGCThreads=<N>設(shè)置并行垃圾回收的線程數(shù)趁仙。此值可以設(shè)置與機(jī)器的CPU處理器數(shù)量相等洪添。
并行收集器可以進(jìn)行如下配置:
最大垃圾回收暫停:指定垃圾回收時(shí)的最長(zhǎng)暫停時(shí)間,通過-XX:MaxGCPauseMillis=<N>指定雀费,<N>為毫秒干奢。如果指定了此值的話,堆大小和垃圾回收相關(guān)參數(shù)會(huì)迕行調(diào)整以達(dá)到指定值盏袄。設(shè)定此值可能會(huì)減少應(yīng)用的吞吐量忿峻。
吞吐量:吞吐量為垃圾回收時(shí)間和非垃圾回收時(shí)間的比值,通過-XX:GCTimeRatio=<N>來設(shè)定辕羽,公式為1/(1+N)逛尚。例如,-XX:GCTimeRatio=19時(shí)刁愿,表示5%癿時(shí)間用于垃圾回收黑低。默認(rèn)請(qǐng)求為99,即1%的時(shí)間用于垃圾回收酌毡。
3、并發(fā)收集
并發(fā)收集可以保證大部分垃圾回收工作都并發(fā)進(jìn)行(應(yīng)用不停止)蕾管,垃圾回收只暫停很少的時(shí)間枷踏,并發(fā)收集器適合于對(duì)響應(yīng)時(shí)間要求比較高的中、大規(guī)模應(yīng)用掰曾,使用-XX:+UseConcMarkSweepGC打開旭蠕。
并發(fā)收集器主要減少年老代的暫停時(shí)間,他在應(yīng)用不停止的情況下使用獨(dú)立的垃圾回收線程旷坦,跟蹤可達(dá)對(duì)象掏熬。在每個(gè)年老代垃圾回收周期中,在收集初期并發(fā)收集器的過程中會(huì)對(duì)整個(gè)應(yīng)用進(jìn)行簡(jiǎn)短的暫停秒梅,在收集中還會(huì)再暫停一次旗芬。第二次暫停會(huì)比第一次稍長(zhǎng),在此過程中多個(gè)線程同時(shí)進(jìn)行垃圾回收工作捆蜀。并發(fā)收集器使用處理器換來短暫的停頓時(shí)間疮丛。在一個(gè)N核處理器的機(jī)器上幔嫂,并發(fā)收集部分使用N/K個(gè)可用處理器進(jìn)行回收,一般情況下1<=K<=N/4誊薄。在只有一個(gè)處理器的主機(jī)上使用并發(fā)收集器履恩,設(shè)置為incremental mode模式也可獲得較短的停頓時(shí)間。
浮動(dòng)垃圾:由于在應(yīng)用運(yùn)行的同時(shí)進(jìn)行垃圾回收呢蔫,所以有些垃圾可能在垃圾回收進(jìn)行完成時(shí)產(chǎn)生切心,這樣就造成了“Floating Garbage”,這些垃圾需要在下次垃圾回收周期時(shí)才能回收掉片吊。所以绽昏,并發(fā)收集器一般需要20%的預(yù)留空間用于這些浮勱垃圾。
并發(fā)模式失敹瘛:并發(fā)收集器在應(yīng)用運(yùn)行時(shí)進(jìn)行收集而涉,所以需要保證堆內(nèi)存在垃圾回收的這段時(shí)間有足夠的空間供程序使用,否則联予,垃圾回收還未完成啼县,堆空間先滿了。返種情況下將會(huì)發(fā)生“Concurrent Mode Failure”沸久,此時(shí)整個(gè)應(yīng)用將會(huì)暫停季眷,進(jìn)行垃圾回收。
啟動(dòng)并發(fā)收集器:因?yàn)椴l(fā)收集在應(yīng)用運(yùn)行時(shí)進(jìn)行收集卷胯,所以必須保證收集完成之前有足夠癿內(nèi)存空間供程序使用子刮,否則會(huì)出現(xiàn)“Concurrent Mode Failure”。通過設(shè)置-XX:CMSInitiatingOccupancyFraction=<N>指定還有多少剩余堆時(shí)開始執(zhí)行并發(fā)收集窑睁。
小結(jié):
串行處理器:
- 適用情況:數(shù)據(jù)量比較型ο俊(100M左右),單處理器下并且對(duì)響應(yīng)時(shí)間無要求的應(yīng)用担钮。
- 缺點(diǎn):只能用亍小型應(yīng)用
并行處理器:
- 適用情況:對(duì)吞吐量有高要求橱赠,多CPU、對(duì)應(yīng)用響應(yīng)時(shí)間無要求的中箫津、大型應(yīng)用狭姨。舉例:后臺(tái)處理、科學(xué)計(jì)算苏遥。
- 缺點(diǎn):垃圾收集過程中應(yīng)用響應(yīng)時(shí)間可能加長(zhǎng)饼拍。
并發(fā)處理器:
- 適用情況:對(duì)響應(yīng)時(shí)間有高要求,多CPU田炭、對(duì)應(yīng)用響應(yīng)時(shí)間有較高要求的中师抄、大型應(yīng)用。舉例:Web服務(wù)器/應(yīng)用服務(wù)器教硫、電信交換司澎、集成開發(fā)環(huán)境欺缘。
七、JVM內(nèi)存監(jiān)控工具
1挤安、命令行工具
- jmap:用于生成堆快照(heapdump)谚殊,堆棧中的對(duì)象的統(tǒng)計(jì)信息,包含類蛤铜、實(shí)例數(shù)量和合計(jì)容量嫩絮。
- jstat:用于監(jiān)控虛擬機(jī)的各種運(yùn)行狀態(tài)信息,如類的裝載围肥、內(nèi)存剿干、垃圾回收、JIT編譯器等穆刻,在沒有GUI的服務(wù)器上置尔。
- jstack:用于JVM當(dāng)前時(shí)刻的線程快照,又稱threaddump文件氢伟,它是JVM當(dāng)前每一條線程正在執(zhí)行的堆棧信息的集合榜轿。
- jstatd:是一個(gè)基于RMI(Remove Method Invocation)的服務(wù)程序,它用于監(jiān)控基于HotSpot的JVM中資源的創(chuàng)建及銷毀朵锣,并且提供了一個(gè)遠(yuǎn)程接口允許遠(yuǎn)程的監(jiān)控工具連接到本地的JVM執(zhí)行命令谬盐。
- jdb:用來對(duì)core文件和正在運(yùn)行的Java進(jìn)程進(jìn)行實(shí)時(shí)地調(diào)試,里面包含了豐富的命令幫助您進(jìn)行調(diào)試诚些。
- jhat:用來分析dump文件的一個(gè)微型的HTTP/HTML服務(wù)器飞傀,它能將生成的dump文件生成在線的HTML文件,讓我們可以通過瀏覽器進(jìn)行查閱诬烹。
- jinfo:實(shí)時(shí)查看虛擬機(jī)的各項(xiàng)參數(shù)信息砸烦,查看默認(rèn)的參數(shù)信息。
- jps:參照Unix系統(tǒng)的取名規(guī)則命名的绞吁,而他的功能和ps的功能類似外冀,可以列舉正在運(yùn)行的JAVA虛擬機(jī)進(jìn)程并顯示虛擬機(jī)執(zhí)行的主類以及這些進(jìn)程的唯一ID。
2掀泳、圖形化工具
- jconsole:一個(gè)基于JMX的GUI工具,用于連接正在運(yùn)行的JVM西轩,它是Java自帶的簡(jiǎn)單性能監(jiān)控工具员舵。
- jvisualvm:是Netbeans的profile子項(xiàng)目,已在JDK6.0 update 7 中自帶(bin/jvisualvm.exe)藕畔,能夠監(jiān)控線程马僻,內(nèi)存情況,查看方法的CPU時(shí)間和內(nèi)存中的對(duì)象注服,已被GC的對(duì)象韭邓,反向查看分配的堆棧(如100個(gè)String對(duì)象分別由哪幾個(gè)對(duì)象分配出來的)措近。
- jprofiler:是一款Java的性能監(jiān)控工具∨纾可以查看當(dāng)前應(yīng)用的對(duì)象瞭郑、對(duì)象引用、內(nèi)存鸭你、CPU使用情況屈张,線程運(yùn)行情況(阻塞、等待等)袱巨,同時(shí)可以查找哪個(gè)對(duì)象占用的內(nèi)存比較多阁谆、哪個(gè)對(duì)象占用CPU處理的時(shí)間比較多。
JVM——當(dāng)我們遇到這個(gè)挑戰(zhàn)Java高薪躲不過的坎時(shí)愉老,很多人都難逃被暴虐的命運(yùn)场绿。原因很多,即使我們制定了學(xué)習(xí)計(jì)劃嫉入,也依然面臨著這些問題
怎么破焰盗?
想要真正的把JVM搞懂,就需要對(duì)JVM有一個(gè)系統(tǒng)的知識(shí)體系的支撐的:
扎實(shí)的JVM基礎(chǔ)知識(shí)劝贸、熟悉JVM內(nèi)存結(jié)構(gòu)姨谷、對(duì)GC垃圾回收有清晰的認(rèn)知、JVM優(yōu)化該考慮到的方面映九、對(duì)JMM的了解缺一不可梦湘。
我們先來看看大廠招聘的要求:這時(shí)候,你是缺少相關(guān)經(jīng)驗(yàn)而毫無頭緒件甥,只能毫無存在感得聽著捌议,還是有著清晰的思路,娓娓道來引有,你愿意做哪個(gè)瓣颅?
本文總結(jié):
看到這有多少人感同身受?我中招了譬正,渾渾噩噩的往復(fù)宫补,默默看著理想老去,難道這是當(dāng)初走上“攻城獅”道路時(shí)想要的生活曾我?
對(duì)JVM感興趣的朋友可以加入:
Java進(jìn)階架構(gòu)技術(shù)群群號(hào):529722406
在群內(nèi)拿些資料視頻以及文檔粉怕,
主要交流Java互聯(lián)網(wǎng)前沿技術(shù)性能調(diào)優(yōu) (Tomcat Nginx JVM) 分布式框架(并發(fā)編程 Zookeeper Netty dubbo Redis)微服務(wù)框架( Spring Cloud Docker虛擬化 微服務(wù)架構(gòu) )
謝謝! 感謝關(guān)注抒巢,歡迎轉(zhuǎn)發(fā)贫贝、