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