jvm 架構(gòu)及優(yōu)化
寫(xiě)在前面
先看一下知乎上一個(gè)有趣的問(wèn)題:
Java工程師面試的時(shí)候,總是提問(wèn)一些jvm如何優(yōu)化的問(wèn)題质礼,這些真的在開(kāi)發(fā)中有用嗎兔院,工作七年了項(xiàng)目中從來(lái)沒(méi)有用過(guò)零聚,并且我獲得過(guò)多次優(yōu)秀員工,望做過(guò)優(yōu)化的大牛解答?
答一:
JVM優(yōu)化肯定是有用的煎谍,可能只是題主沒(méi)有遇到過(guò)這方面的需求攘蔽。比如一些GC機(jī)制會(huì)引起JVM的Stop The World,也就是所有工作線程都會(huì)停下來(lái)等待GC完成呐粘。對(duì)于一些對(duì)延遲比較敏感的程序來(lái)說(shuō)满俗,這一停頓達(dá)到一百甚至是幾十毫秒的時(shí)候就是難以接受的。為了解決這類(lèi)問(wèn)題事哭,就需要對(duì)JVM的參數(shù)做適當(dāng)?shù)恼{(diào)整漫雷。比如調(diào)整堆的大小,選擇合適的垃圾回收器鳍咱,控制對(duì)象晉升老年代的速度等等降盹。
作者:謝知恒
鏈接:https://www.zhihu.com/question/40913700/answer/88862720
來(lái)源:知乎
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)谤辜,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處蓄坏。
答二:
對(duì)于樓主這種情況也是可以理解的价捧,以前我在傳統(tǒng)行業(yè)做行業(yè)軟件的時(shí)候也是對(duì)jvm一無(wú)所知,覺(jué)得一點(diǎn)用沒(méi)有涡戳,面試問(wèn)只是裝逼而已结蟋。后來(lái)踏入互聯(lián)網(wǎng)行業(yè)才明白,這是必須掌握的渔彰,而且經(jīng)常會(huì)用嵌屎,遇到tp99間斷性的提高,這種情況首先就會(huì)去看是否是fullGC引起的恍涂,還有就是內(nèi)存溢出之類(lèi)的問(wèn)題宝惰,不了解jvm怎么去解決這類(lèi)問(wèn)題。當(dāng)然傳統(tǒng)行業(yè)軟件再沧,用戶量少可能不會(huì)去監(jiān)控性能尼夺,遇到內(nèi)存溢出可能也就重啟解決了,不了了之炒瘸,下次再重啟淤堵。然而,對(duì)于java開(kāi)發(fā)人員來(lái)說(shuō)顷扩,jvm就是一座商廈拐邪,我們?cè)诶锩骈_(kāi)店,連消防栓在哪里都不知道屎即,哪天著火了庙睡,應(yīng)急樓梯也不知道在哪里事富,只能坐著哭技俐?所以還是建議樓主了解一下jvm,推薦看《深入理解java虛擬機(jī)》
作者:周易
鏈接:https://www.zhihu.com/question/40913700/answer/138011891
來(lái)源:知乎
著作權(quán)歸作者所有统台。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)雕擂,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
從上面兩段回答中可以看出來(lái)贱勃,熟悉jvm架構(gòu)和調(diào)優(yōu)意義在于出了性能問(wèn)題后井赌,多給程序員留了一條退路。
jvm 架構(gòu)
首先先附上一張jvm架構(gòu)圖贵扰。
上圖中主要由幾個(gè)部分組成:
- 類(lèi)加載器(ClassLoader):在JVM啟動(dòng)時(shí)或者在類(lèi)運(yùn)行時(shí)將需要的class加載到JVM中仇穗。每當(dāng)運(yùn)行一個(gè)
java 程序時(shí),就會(huì)首先動(dòng)態(tài)創(chuàng)建相應(yīng)的jvm instance(啟動(dòng)三個(gè)程序戚绕,就會(huì)有三個(gè)jvm實(shí)例纹坐,這點(diǎn)和golang runtime非常類(lèi)似),然后將該程序的所有類(lèi)文件通過(guò)類(lèi)加載器加載到j(luò)vm中舞丛。 - 執(zhí)行引擎:負(fù)責(zé)執(zhí)行class文件中包含的字節(jié)碼指令.執(zhí)行引擎是java 虛擬機(jī)模擬運(yùn)行的運(yùn)算引擎耘子,基本起到了翻譯的作用果漾。
有兩種翻譯的方式:- 一句話一句話翻譯。也就是解釋執(zhí)行谷誓。將程序計(jì)算器中指向的待執(zhí)行的java字節(jié)碼翻譯為cpu可以運(yùn)行的機(jī)器指令绒障。
- 一次性翻譯。即編譯執(zhí)行捍歪。通過(guò)JIT(just in time)一次性將所有的字節(jié)碼翻譯完成户辱。
- 內(nèi)存區(qū)(也叫運(yùn)行時(shí)數(shù)據(jù)區(qū)):是在JVM運(yùn)行的時(shí)候操作所分配的內(nèi)存區(qū)。從上圖中可以看出糙臼,主要分為五個(gè)部分焕妙。
- 本地庫(kù)接口,本地方法庫(kù):操作系統(tǒng)所有弓摘,用于處理jvm的native code和通過(guò)jit編譯后的本地代碼焚鹊。
jvm 執(zhí)行引擎
關(guān)于jvm的執(zhí)行引擎,它并不是真正軟件模擬實(shí)現(xiàn)了cpu韧献,最終所有計(jì)算機(jī)上的運(yùn)算都是由cpu執(zhí)行機(jī)器指令來(lái)處理末患。
有個(gè)問(wèn)題?jvm執(zhí)行引擎和本地代碼關(guān)系如何锤窑?本地代碼還需要過(guò)jvm執(zhí)行引擎嗎璧针?
猜測(cè)答案:應(yīng)該是需要過(guò)的,jvm封裝了所有的代碼渊啰,管理著程序執(zhí)行的結(jié)果探橱。
關(guān)于執(zhí)行引擎采用哪種方式來(lái)運(yùn)行,經(jīng)驗(yàn)如下:
用JIT編譯器來(lái)編譯代碼所花的時(shí)間要比用解釋器去一條條解釋執(zhí)行花的時(shí)間要多绘证。因此隧膏,如果代碼只被執(zhí)行一次的話,那么最好還是解釋執(zhí)行而不是編譯后再執(zhí)行嚷那。因此胞枕,內(nèi)置了JIT編譯器的JVM都會(huì)檢查方法的執(zhí)行頻率,如果一個(gè)方法的執(zhí)行頻率超過(guò)一個(gè)特定的值的話魏宽,那么這個(gè)方法就會(huì)被編譯成本地代碼腐泻。
jvm 內(nèi)存
從jvm 架構(gòu)可以看出,jvm內(nèi)存主要分為五部分:
- 方法區(qū)(Method Area):用于存儲(chǔ)類(lèi)結(jié)構(gòu)信息的地方队询,包括常量池派桩、靜態(tài)變量、構(gòu)造函數(shù)等蚌斩。雖然JVM規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分铆惑, 但它卻有個(gè)別名non-heap(非堆),所以大家不要搞混淆了。方法區(qū)還包含一個(gè)運(yùn)行時(shí)常量池鸭津。
- java堆(Heap):存儲(chǔ)java實(shí)例或者對(duì)象的地方彤侍。這塊是GC的主要區(qū)域。從存儲(chǔ)的內(nèi)容我們可以很容易知道逆趋,方法區(qū)和堆是被所有java線程共享的盏阶。
- java棧(Stack):java棧總是和線程關(guān)聯(lián)在一起闻书,每當(dāng)創(chuàng)建一個(gè)線程時(shí)名斟,JVM就會(huì)為這個(gè)線程創(chuàng)建一個(gè)對(duì)應(yīng)的java棧。在這個(gè)java棧中又會(huì)包含多個(gè)棧幀魄眉,每運(yùn)行一個(gè)方法就創(chuàng)建一個(gè)棧幀砰盐,用于存儲(chǔ)局部變量表、操作棧坑律、方法返回值等岩梳。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過(guò)程,就對(duì)應(yīng)一個(gè)棧幀在java棧中入棧到出棧的過(guò)程晃择。所以java棧是現(xiàn)成私有的冀值。
- 程序計(jì)數(shù)器(PC Register):用于保存當(dāng)前線程執(zhí)行的內(nèi)存地址。由于JVM程序是多線程執(zhí)行的(線程輪流切換)宫屠,所以為了保證線程切換回來(lái)后列疗,還能恢復(fù)到原先狀態(tài),就需要一個(gè)獨(dú)立的計(jì)數(shù)器浪蹂,記錄之前中斷的地方抵栈,可見(jiàn)程序計(jì)數(shù)器也是線程私有的。
- 本地方法棧(Native Method Stack):和java棧的作用差不多坤次,只不過(guò)是為JVM使用到的native方法服務(wù)的古劲。
java 堆存放對(duì)象實(shí)例。
java 棧存放普通的變量及其它類(lèi)型的信息浙踢。
這樣的好處是對(duì)象通常占有內(nèi)存很大绢慢,但個(gè)數(shù)少灿渴。棧里存放的東西個(gè)數(shù)多洛波,占用內(nèi)存少。對(duì)象是垃圾回收的主戰(zhàn)場(chǎng)骚露。
所以要分出棧和堆來(lái)蹬挤。
jvm調(diào)優(yōu)
傳統(tǒng)的軟件開(kāi)發(fā)過(guò)程中不需要考慮到j(luò)vm調(diào)優(yōu)的內(nèi)容,主要出于以下的幾點(diǎn)考慮:
- 性能不是生命線棘幸。
- 系統(tǒng)出問(wèn)題后可以采取其它的方式進(jìn)行處理焰扳,例如停機(jī),停機(jī)后再恢復(fù),受影響不大吨悍。
而在互聯(lián)網(wǎng)領(lǐng)域扫茅,上述兩個(gè)問(wèn)題就變?yōu)楸容^嚴(yán)重的問(wèn)題。
為了留住用戶育瓜,響應(yīng)時(shí)間和吞吐量就會(huì)成為必須去解決的問(wèn)題葫隙。而jvm性能瓶頸主要在與jvm自帶的垃圾回收機(jī)制。
內(nèi)存分配機(jī)制
靜態(tài)內(nèi)存&動(dòng)態(tài)內(nèi)存
Java的內(nèi)存分配原理與C/C++不同躏仇,C/C++每次申請(qǐng)內(nèi)存時(shí)都要malloc進(jìn)行系統(tǒng)調(diào)用恋脚,而系統(tǒng)調(diào)用發(fā)生在內(nèi)核空間,每次都要中斷進(jìn)行切換焰手,這需要一定的開(kāi)銷(xiāo)糟描,
而Java虛擬機(jī)是先一次性分配一塊較大的空間,然后每次new時(shí)都在該空間上進(jìn)行分配和釋放书妻,減少了系統(tǒng)調(diào)用的次數(shù)船响,節(jié)省了一定的開(kāi)銷(xiāo),這有點(diǎn)類(lèi)似于內(nèi)存池的概念躲履;二是有了這塊空間過(guò)后灿意,如何進(jìn)行分配和回收就跟GC機(jī)制有關(guān)了。
java一般內(nèi)存申請(qǐng)有兩種:
- 靜態(tài)內(nèi)存.編譯時(shí)就能夠確定的內(nèi)存就是靜態(tài)內(nèi)存崇呵,即內(nèi)存是固定的缤剧,系統(tǒng)一次性分配,比如int類(lèi)型變量域慷;java棧荒辕、程序計(jì)數(shù)器、本地方法棧都是線程私有的犹褒,線程生就生抵窒,線程滅就滅,棧中的棧幀隨著方法的結(jié)束也會(huì)撤銷(xiāo)叠骑,內(nèi)存自然就跟著回收了李皇。我們不需要管的。
- 動(dòng)態(tài)內(nèi)存宙枷。動(dòng)態(tài)內(nèi)存分配就是在程序執(zhí)行時(shí)才知道要分配的存儲(chǔ)空間大小掉房,比如java對(duì)象的內(nèi)存空間。所以這幾個(gè)區(qū)域的內(nèi)存分配與回收是確定的慰丛,但是java堆和方法區(qū)則不一樣卓囚,我們只有在程序運(yùn)行期間才知道會(huì)創(chuàng)建哪些對(duì)象,所以這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的诅病。一般我們所說(shuō)的垃圾回收也是針對(duì)的這一部分哪亿。
總之Stack的內(nèi)存管理是順序分配的粥烁,而且定長(zhǎng),不存在內(nèi)存回收問(wèn)題蝇棉;而Heap 則是為java對(duì)象的實(shí)例隨機(jī)分配內(nèi)存讨阻,不定長(zhǎng)度,所以存在內(nèi)存分配和回收的問(wèn)題篡殷;
新生代&老生代
在 Java 中变勇,堆被劃分成兩個(gè)不同的區(qū)域:
- 新生代 ( Young )。 生命周期較短贴唇。
- 老年代 ( Old )搀绣。生命周期較長(zhǎng)。
- 永久代戳气。很少被討論链患。
新生代 ( Young ) 又被劃分為三個(gè)區(qū)域:
- Eden
- From Survivor
- To Survivor。
GC機(jī)制簡(jiǎn)述
JVM 使用的GC算法是什么瓶您?
分代收集:即將內(nèi)存分為幾個(gè)區(qū)域麻捻,將不同生命周期的對(duì)象放在不同區(qū)域里。GC根據(jù)用途來(lái)說(shuō)有以下三種:
GC(或Minor GC):收集 生命周期短的區(qū)域(Young area)呀袱。Minor GC會(huì)把Eden中的所有活的對(duì)象都移到Survivor區(qū)域中贸毕,如果Survivor區(qū)中放不下,那么剩下的活的對(duì)象就被移到Old generation 中夜赵。
Full GC (或Major GC):基于標(biāo)記-清除算法明棍,收集生命周期短的區(qū)域(Young area)和生命周期比較長(zhǎng)的區(qū)域(Old area)對(duì)整個(gè)堆進(jìn)行垃圾收集。
Major GC :清理永久代寇僧。
GC 效率也會(huì)比較高摊腋,我們要盡量減少 Full GC 的次數(shù)。
在minor Gc 與Full Gc執(zhí)行機(jī)制上嘁傀,都提供了三種選擇:
- 串行GC(SerialGC)
- 并行回收GC(ParallelScavenge
- 并行GC(ParNew)
可以根據(jù)具體的需要選擇相應(yīng)的GC兴蒸。
GC的調(diào)整是在吞吐量和響應(yīng)時(shí)間上做一個(gè)平衡。
jvm調(diào)優(yōu)簡(jiǎn)述與工具
怎樣調(diào)優(yōu)?
有何工具?
Jvm調(diào)優(yōu)工具有以下幾種:
- Jconsole : jdk自帶细办,功能簡(jiǎn)單橙凳,但是可以在系統(tǒng)有一定負(fù)荷的情況下使用。對(duì)垃圾回收算法有很詳細(xì)的跟蹤笑撞。詳細(xì)說(shuō)明參考這里
- JProfiler:商業(yè)軟件岛啸,需要付費(fèi)。功能強(qiáng)大娃殖。詳細(xì)說(shuō)明參考這里
- VisualVM:JDK自帶值戳,功能強(qiáng)大,與JProfiler類(lèi)似炉爆。推薦。
關(guān)于調(diào)優(yōu)這塊在實(shí)際業(yè)務(wù)中碰到這類(lèi)的問(wèn)題再進(jìn)行具體分析。