對(duì)于搞開(kāi)發(fā)的我們其實(shí)也是一樣,,現(xiàn)在流行的框架越來(lái)越多,,封裝的也越來(lái)越完善,,各種框架可以搞定一切,,幾乎不用關(guān)注底層的實(shí)現(xiàn),,初級(jí)程序員只要熟悉基本的使用方法,,便可以快速的開(kāi)發(fā)上線;但對(duì)于高級(jí)程序員來(lái)講,,內(nèi)功的修煉卻越發(fā)的重要,,比如算法、設(shè)計(jì)模式,、底層原理等,,只有把這些基礎(chǔ)熟練之后,才能在開(kāi)發(fā)過(guò)程中知其然知其所以然,,出現(xiàn)問(wèn)題時(shí)能快速定位到問(wèn)題的本質(zhì),。 對(duì)于Java程序員來(lái)講,spring全家桶幾乎可以搞定一切,,spring全家桶便是精妙的招式,,jvm就是內(nèi)功心法很重要的一塊,線上出現(xiàn)性能問(wèn)題,,jvm調(diào)優(yōu)更是不可回避的問(wèn)題,。因此JVM基礎(chǔ)知識(shí)對(duì)于高級(jí)程序員的重要性不必言語(yǔ). 一.jvm體系總體分四大塊: 1.類(lèi)的加載機(jī)制 2.jvm內(nèi)存結(jié)構(gòu) 3.GC算法 垃圾回收 4.GC分析 命令調(diào)優(yōu) 二.類(lèi)的加載機(jī)制 1.什么是類(lèi)的加載 2.類(lèi)的生命周期 3.類(lèi)加載器 4.雙親委派模型 三.什么是類(lèi)的加載 類(lèi)的加載指的是將類(lèi)的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,將其放在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi),,然后在堆區(qū)創(chuàng)建一個(gè)java.lang.Class對(duì)象,,用來(lái)封裝類(lèi)在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu),。類(lèi)的加載的最終產(chǎn)品是位于堆區(qū)中的Class對(duì)象,Class對(duì)象封裝了類(lèi)在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu),,并且向Java程序員提供了訪問(wèn)方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口,。 四.類(lèi)的生命周期 1.加載,查找并加載類(lèi)的二進(jìn)制數(shù)據(jù),,在Java堆中也創(chuàng)建一個(gè)java.lang.Class類(lèi)的對(duì)象 2.連接,,連接又包含三塊內(nèi)容:驗(yàn)證、準(zhǔn)備,、初始化,。1)驗(yàn)證,文件格式,、元數(shù)據(jù),、字節(jié)碼、符號(hào)引用驗(yàn)證,;2)準(zhǔn)備,,為類(lèi)的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值,;3)解析,,把類(lèi)中的符號(hào)引用轉(zhuǎn)換為直接引用 3.初始化,為類(lèi)的靜態(tài)變量賦予正確的初始值 4.使用,,new出對(duì)象程序中使用 5.卸載,,執(zhí)行垃圾回收 五.類(lèi)加載器 1.啟動(dòng)類(lèi)加載器:Bootstrap ClassLoader,負(fù)責(zé)加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,,下同)下,,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類(lèi)庫(kù) 2.擴(kuò)展類(lèi)加載器:Extension ClassLoader,,該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),,它負(fù)責(zé)加載DK\jre\lib\ext目錄中,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類(lèi)庫(kù)(如javax.*開(kāi)頭的類(lèi)),,開(kāi)發(fā)者可以直接使用擴(kuò)展類(lèi)加載器,。 3.應(yīng)用程序類(lèi)加載器:Application ClassLoader,該類(lèi)加載器由sun.misc.Launcher$AppClassLoader來(lái)實(shí)現(xiàn),,它負(fù)責(zé)加載用戶類(lèi)路徑(ClassPath)所指定的類(lèi),,開(kāi)發(fā)者可以直接使用該類(lèi)加載器 六.類(lèi)加載機(jī)制 1.全盤(pán)負(fù)責(zé),當(dāng)一個(gè)類(lèi)加載器負(fù)責(zé)加載某個(gè)Class時(shí),,該Class所依賴(lài)的和引用的其他Class也將由該類(lèi)加載器負(fù)責(zé)載入,,除非顯示使用另外一個(gè)類(lèi)加載器來(lái)載入 2.父類(lèi)委托,先讓父類(lèi)加載器試圖加載該類(lèi),,只有在父類(lèi)加載器無(wú)法加載該類(lèi)時(shí)才嘗試從自己的類(lèi)路徑中加載該類(lèi) 3.緩存機(jī)制,,緩存機(jī)制將會(huì)保證所有加載過(guò)的Class都會(huì)被緩存,,當(dāng)程序中需要使用某個(gè)Class時(shí),類(lèi)加載器先從緩存區(qū)尋找該Class,,只有緩存區(qū)不存在,,系統(tǒng)才會(huì)讀取該類(lèi)對(duì)應(yīng)的二進(jìn)制數(shù)據(jù),并將其轉(zhuǎn)換成Class對(duì)象,,存入緩存區(qū),。這就是為什么修改了Class后,必須重啟JVM,,程序的修改才會(huì)生效 七.jvm內(nèi)存結(jié)構(gòu) 1.方法區(qū)和對(duì)是所有線程共享的內(nèi)存區(qū)域,;而java棧、本地方法棧和程序員計(jì)數(shù)器是運(yùn)行是線程私有的內(nèi)存區(qū)域,。 2.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)存。 3.方法區(qū)(Method Area),方法區(qū)(Method Area)與Java堆一樣,,是各個(gè)線程共享的內(nèi)存區(qū)域,,它用于存儲(chǔ)已被虛擬機(jī)加載的類(lèi)信息、常量,、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù),。 4.程序計(jì)數(shù)器(Program Counter Register),程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。 5.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í)行完成的過(guò)程,,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過(guò)程。 6.本地方法棧(Native Method Stacks),本地方法棧(Native Method Stacks)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,,其區(qū)別不過(guò)是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),,而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。 八.對(duì)象分配規(guī)則 1.對(duì)象優(yōu)先分配在Eden區(qū),,如果Eden區(qū)沒(méi)有足夠的空間時(shí),,虛擬機(jī)執(zhí)行一次Minor GC,。 2.大對(duì)象直接進(jìn)入老年代(大對(duì)象是指需要大量連續(xù)內(nèi)存空間的對(duì)象)。這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存拷貝(新生代采用復(fù)制算法收集內(nèi)存),。 3.長(zhǎng)期存活的對(duì)象進(jìn)入老年代,。虛擬機(jī)為每個(gè)對(duì)象定義了一個(gè)年齡計(jì)數(shù)器,如果對(duì)象經(jīng)過(guò)了1次Minor GC那么對(duì)象會(huì)進(jìn)入Survivor區(qū),,之后每經(jīng)過(guò)一次Minor GC那么對(duì)象的年齡加1,,知道達(dá)到閥值對(duì)象進(jìn)入老年區(qū)。 4.動(dòng)態(tài)判斷對(duì)象的年齡,。如果Survivor區(qū)中相同年齡的所有對(duì)象大小的總和大于Survivor空間的一半,,年齡大于或等于該年齡的對(duì)象可以直接進(jìn)入老年代。 5.空間分配擔(dān)保,。每次進(jìn)行Minor GC時(shí),,JVM會(huì)計(jì)算Survivor區(qū)移至老年區(qū)的對(duì)象的平均大小,如果這個(gè)值大于老年區(qū)的剩余值大小則進(jìn)行一次Full GC,,如果小于檢查HandlePromotionFailure設(shè)置,,如果true則只進(jìn)行Monitor GC,如果false則進(jìn)行Full GC。 九.GC算法 GC最基礎(chǔ)的算法有三種:標(biāo)記 -清除算法,、復(fù)制算法,、標(biāo)記-壓縮算法,我們常用的垃圾回收器一般都采用分代收集算法,。 1.標(biāo)記 -清除算法,,“標(biāo)記-清除”(Mark-Sweep)算法,如它的名字一樣,,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象。 2.復(fù)制算法,,“復(fù)制”(Copying)的收集算法,,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊,。當(dāng)這一塊的內(nèi)存用完了,,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過(guò)的內(nèi)存空間一次清理掉,。 3.標(biāo)記-壓縮算法,,標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,,而是讓所有存活的對(duì)象都向一端移動(dòng),,然后直接清理掉端邊界以外的內(nèi)存 4.分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分為新生代和老年代,,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?br> 十.垃圾回收器 1.Serial收集器,,串行收集器是最古老,最穩(wěn)定以及效率高的收集器,,可能會(huì)產(chǎn)生較長(zhǎng)的停頓,,只使用一個(gè)線程去回收,。 2.ParNew收集器,,ParNew收集器其實(shí)就是Serial收集器的多線程版本。 3.Parallel收集器,,Parallel Scavenge收集器類(lèi)似ParNew收集器,,Parallel收集器更關(guān)注系統(tǒng)的吞吐量。 4.Parallel Old 收集器,,Parallel Old是Parallel Scavenge收集器的老年代版本,,使用多線程和“標(biāo)記-整理”算法 5.CMS收集器,CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器,。 6.G1收集器,,G1 (Garbage-First)是一款面向服務(wù)器的垃圾收集器,主要針對(duì)配備多顆處理器及大容量?jī)?nèi)存的機(jī)器. 以極高概率滿足GC停頓時(shí)間要求的同時(shí),還具備高吞吐量性能特征 7.GC算法和垃圾回收器算法圖解以及更詳細(xì)內(nèi)容參考JVM(3):Java GC算法 垃圾收集器 十一.GC日志分析 摘錄GC日志一部分(前部分為年輕代gc回收;后部分為full gc回收): 2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs] 2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs] 通過(guò)上面日志分析得出,,PSYoungGen、ParOldGen,、PSPermGen屬于Parallel收集器,。其中PSYoungGen表示gc回收前后年輕代的內(nèi)存變化,;ParOldGen表示gc回收前后老年代的內(nèi)存變化;PSPermGen表示gc回收前后永久區(qū)的內(nèi)存變化,。young gc 主要是針對(duì)年輕代進(jìn)行內(nèi)存回收比較頻繁,,耗時(shí)短,;full gc 會(huì)對(duì)整個(gè)堆內(nèi)存進(jìn)行回城,耗時(shí)長(zhǎng),,因此一般盡量減少full gc的次數(shù) 十二.調(diào)優(yōu)命令 Sun JDK監(jiān)控和故障處理命令有jps jstat jmap jhat jstack jinfo 1.jps,JVM Process Status Tool,顯示指定系統(tǒng)內(nèi)所有的HotSpot虛擬機(jī)進(jìn)程,。 2.jstat,JVM statistics Monitoring是用于監(jiān)視虛擬機(jī)運(yùn)行時(shí)狀態(tài)信息的命令,,它可以顯示出虛擬機(jī)進(jìn)程中的類(lèi)裝載,、內(nèi)存,、垃圾收集、JIT編譯等運(yùn)行數(shù)據(jù),。 3.jmap,,JVM Memory Map命令用于生成heap dump文件 4.jhat,JVM Heap Analysis Tool命令是與jmap搭配使用,,用來(lái)分析jmap生成的dump,,jhat內(nèi)置了一個(gè)微型的HTTP/HTML服務(wù)器,生成dump的分析結(jié)果后,,可以在瀏覽器中查看 5.jstack,用于生成java虛擬機(jī)當(dāng)前時(shí)刻的線程快照,。 6.jinfo,,JVM Configuration info 這個(gè)命令作用是實(shí)時(shí)查看和調(diào)整虛擬機(jī)運(yùn)行參數(shù)。 詳細(xì)的命令使用參考這里JVM(4):Jvm調(diào)優(yōu)-命令篇 十三.調(diào)優(yōu)工具 常用調(diào)優(yōu)工具分為兩類(lèi),jdk自帶監(jiān)控工具:jconsole和jvisualvm,,第三方有:MAT(Memory Analyzer Tool),、GChisto。 1.jconsole,,Java Monitoring and Management Console是從java5開(kāi)始,,在JDK中自帶的java監(jiān)控和管理控制臺(tái),用于對(duì)JVM中內(nèi)存,,線程和類(lèi)等的監(jiān)控 2.jvisualvm,,jdk自帶全能工具,可以分析內(nèi)存快照,、線程快照,;監(jiān)控內(nèi)存變化、GC變化等,。 3.MAT,,Memory Analyzer Tool,一個(gè)基于Eclipse的內(nèi)存分析工具,,是一個(gè)快速,、功能豐富的Java heap分析工具,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗 4.GChisto,,一款專(zhuān)業(yè)分析gc日志的工具 |
|