JVM 原理探索

一.類加載機(jī)制

1.類的加載:類的加載指的是將類的.class 文件中的二進(jìn)制數(shù)據(jù)讀取到內(nèi)存中遂鹊,并放在方法去內(nèi)勘纯,然后堆區(qū)創(chuàng)建一個(gè) java.lang.Class對(duì)象址芯,用來封裝在方法去內(nèi)的數(shù)據(jù)結(jié)構(gòu)。類加載的最終產(chǎn)品就是Class 對(duì)象鞠评,向程序員提供了訪問方法的數(shù)據(jù)結(jié)構(gòu)接口脱茉。
2.類的生命周期:類加載的過程包括了加載剪芥、驗(yàn)證、準(zhǔn)備琴许、解析(運(yùn)行時(shí)綁定粗俱,就是說可能在初始化之后解析)、初始化五個(gè)階段虚吟。
3.類的加載三種方式:
  • 命令行啟動(dòng)應(yīng)用時(shí)候由JVM初始化加載
  • 通過Class.forName()方法動(dòng)態(tài)加載
  • 通過ClassLoader.loadClass()方法動(dòng)態(tài)加載
package com.neo.classloader;
public class loaderTest { 
        public static void main(String[] args) throws ClassNotFoundException { 
                ClassLoader loader = HelloWorld.class.getClassLoader(); 
                System.out.println(loader); 
                //使用ClassLoader.loadClass()來加載類寸认,不會(huì)執(zhí)行初始化塊 
                loader.loadClass("Test2"); 
                //使用Class.forName()來加載類,默認(rèn)會(huì)執(zhí)行初始化塊 
                //Class.forName("Test2"); 
                //使用Class.forName()來加載類串慰,并指定ClassLoader偏塞,初始化時(shí)不執(zhí)行靜態(tài)塊 
                //Class.forName("Test2", false, loader); 
        } 
}

Class.forName()和ClassLoader.loadClass()區(qū)別

Class.forName():將類的.class文件加載到j(luò)vm中之外,還會(huì)對(duì)類進(jìn)行解釋邦鲫,執(zhí)行類中的static塊灸叼;
ClassLoader.loadClass():只干一件事情,就是將.class文件加載到j(luò)vm中庆捺,不會(huì)執(zhí)行static中的內(nèi)容,只有在newInstance才會(huì)去執(zhí)行static塊古今。
Class.forName(name, initialize, loader)帶參函數(shù)也可控制是否加載static塊。并且只有調(diào)用了newInstance()方法采用調(diào)用構(gòu)造函數(shù)滔以,創(chuàng)建類的對(duì)象 捉腥。

4.雙親委派模型:雙親委派模型的工作流程是:如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類你画,而是把請(qǐng)求委托給父加載器去完成抵碟,依次向上,因此坏匪,所有的類加載請(qǐng)求最終都應(yīng)該被傳遞到頂層的啟動(dòng)類加載器中拟逮,只有當(dāng)父加載器在它的搜索范圍中沒有找到所需的類時(shí),即無(wú)法完成該加載适滓,子加載器才會(huì)嘗試自己去加載該類敦迄。

雙親委派機(jī)制:

1、當(dāng)AppClassLoader加載一個(gè)class時(shí)凭迹,它首先不會(huì)自己去嘗試加載這個(gè)類罚屋,而是把類加載請(qǐng)求委派給父類加載器ExtClassLoader去完成。
2蕊苗、當(dāng)ExtClassLoader加載一個(gè)class時(shí)沿后,它首先也不會(huì)自己去嘗試加載這個(gè)類,而是把類加載請(qǐng)求委派給BootStrapClassLoader```去完成朽砰。
3尖滚、如果BootStrapClassLoader加載失敽砹酢(例如在$JAVA_HOME/jre/lib里未查找到該class),會(huì)使用ExtClassLoader來嘗試加載漆弄;
4睦裳、若ExtClassLoader也加載失敗,則會(huì)使用AppClassLoader來加載撼唾,如果AppClassLoader也加載失敗廉邑,則會(huì)報(bào)出異常ClassNotFoundException。
ClassLoader源碼分析:

public Class<?> loadClass(String name)throws ClassNotFoundException {
        return loadClass(name, false);
}

protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
        // 首先判斷該類型是否已經(jīng)被加載
        Class c = findLoadedClass(name);
        if (c == null) {
            //如果沒有被加載倒谷,就委托給父類加載或者委派給啟動(dòng)類加載器加載
            try {
                if (parent != null) {
                     //如果存在父類加載器蛛蒙,就委派給父類加載器加載
                    c = parent.loadClass(name, false);
                } else {
                //如果不存在父類加載器,就檢查是否是由啟動(dòng)類加載器加載的類渤愁,通過調(diào)用本地方法native Class findBootstrapClass(String name)
                    c = findBootstrapClass0(name);
                }
            } catch (ClassNotFoundException e) {
             // 如果父類加載器和啟動(dòng)類加載器都不能完成加載任務(wù)牵祟,才調(diào)用自身的加載功能
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
雙親委派模型意義:

系統(tǒng)類防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼
保證Java程序安全穩(wěn)定運(yùn)行

二.JVM 內(nèi)存結(jié)構(gòu)
JVM內(nèi)存結(jié)構(gòu)主要有三大塊:堆內(nèi)存、方法區(qū)和棧抖格。堆內(nèi)存是JVM中最大的一塊由++年輕代和老年代++組成诺苹,而年輕代內(nèi)存又被分成++三部分++,++Eden空間雹拄、From Survivor空間收奔、To Survivor空間++,默認(rèn)情況下年輕代按照8:1:1的比例來分配;

方法區(qū)存儲(chǔ)類信息滓玖、常量坪哄、靜態(tài)變量等數(shù)據(jù),是線程共享的區(qū)域呢撞,為與Java堆區(qū)分损姜,方法區(qū)還有一個(gè)別名Non-Heap(非堆)饰剥;棧又分為java虛擬機(jī)棧和本地方法棧主要用于方法的執(zhí)行殊霞。
程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器汰蓉。

控制參數(shù)

-Xms設(shè)置堆的最小空間大小绷蹲。
-Xmx設(shè)置堆的最大空間大小。
-XX:NewSize設(shè)置新生代最小空間大小顾孽。
-XX:MaxNewSize設(shè)置新生代最大空間大小祝钢。
-XX:PermSize設(shè)置永久代最小空間大小。
-XX:MaxPermSize設(shè)置永久代最大空間大小若厚。
-Xss設(shè)置每個(gè)線程的堆棧大小

對(duì)象分配規(guī)則

對(duì)象優(yōu)先分配在Eden區(qū)拦英,如果Eden區(qū)沒有足夠的空間時(shí),虛擬機(jī)執(zhí)行一次Minor GC测秸。
大對(duì)象直接進(jìn)入老年代(大對(duì)象是指需要大量連續(xù)內(nèi)存空間的對(duì)象)疤估。這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存拷貝(新生代采用復(fù)制算法收集內(nèi)存)灾常。
長(zhǎng)期存活的對(duì)象進(jìn)入老年代。虛擬機(jī)為每個(gè)對(duì)象定義了一個(gè)年齡計(jì)數(shù)器铃拇,如果對(duì)象經(jīng)過了1次Minor GC那么對(duì)象會(huì)進(jìn)入Survivor區(qū)钞瀑,同時(shí)年齡+1,之后每經(jīng)過一次Minor GC那么對(duì)象的年齡加1慷荔,知道達(dá)到閥值對(duì)象進(jìn)入老年區(qū)雕什。
動(dòng)態(tài)判斷對(duì)象的年齡。如果Survivor區(qū)中相同年齡的所有對(duì)象大小的總和大于Survivor空間的一半显晶,年齡大于或等于該年齡的對(duì)象可以直接進(jìn)入老年代贷岸。
空間分配擔(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 算法:

判斷對(duì)象是否存活一般有兩種方式:

引用計(jì)數(shù):每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1睁本,引用釋放時(shí)計(jì)數(shù)減1尿庐,計(jì)數(shù)為0時(shí)可以回收。此方法簡(jiǎn)單呢堰,無(wú)法解決對(duì)象相互循環(huán)引用的問題抄瑟。
可達(dá)性分析(Reachability Analysis):從GC Roots開始向下搜索,搜索所走過的路徑稱為引用鏈枉疼。當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí)皮假,則證明此對(duì)象是不可用的,不可達(dá)對(duì)象骂维。

GC最基礎(chǔ)的算法有三種:標(biāo)記 -清除算法惹资、復(fù)制算法、標(biāo)記-壓縮算法航闺,我們常用的垃圾回收器一般都采用分代收集算法褪测。

詳細(xì)介紹:

標(biāo)記 -清除算法,“標(biāo)記-清除”(Mark-Sweep)算法潦刃,如它的名字一樣侮措,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象乖杠。
復(fù)制算法分扎,“復(fù)制”(Copying)的收集算法,它將可用內(nèi)存按容量劃分為大小相等的兩塊胧洒,每次只使用其中的一塊畏吓。當(dāng)這一塊的內(nèi)存用完了环揽,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉庵佣。
標(biāo)記-壓縮算法歉胶,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理巴粪,而是讓所有存活的對(duì)象都向一端移動(dòng)通今,然后直接清理掉端邊界以外的內(nèi)存
分代收集算法,“分代收集”(Generational Collection)算法肛根,把Java堆分為新生代和老年代辫塌,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/h6>
垃圾回收器了解:

Serial收集器,串行收集器是最古老派哲,最穩(wěn)定以及效率高的收集器臼氨,可能會(huì)產(chǎn)生較長(zhǎng)的停頓,只使用一個(gè)線程去回收芭届。
ParNew收集器储矩,ParNew收集器其實(shí)就是Serial收集器的多線程版本。
Parallel收集器褂乍,Parallel Scavenge收集器類似ParNew收集器持隧,Parallel收集器更關(guān)注系統(tǒng)的吞吐量。
Parallel Old 收集器逃片,Parallel Old是Parallel Scavenge收集器的老年代版本屡拨,使用多線程和“標(biāo)記-整理”算法
CMS收集器,CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器褥实。
G1 收集器呀狼,是一款面向服務(wù)器的垃圾收集器,主要針對(duì)配備多顆處理器及大容量?jī)?nèi)存的機(jī)器损离,以極高概率滿足 GC 停頓時(shí)間要求的同時(shí)哥艇,還具備高吞吐量性能特征。

三.JVM 調(diào)優(yōu)

jvm監(jiān)控分析工具一般分為兩類草冈,一種是jdk自帶的工具她奥,一種是第三方的分析工具。jdk自帶工具一般在jdk bin目錄下面怎棱,jconsole.exe和jvisualvm.exe;第三方的分析工具有很多绷跑,各自的側(cè)重點(diǎn)不同拳恋,比較有代表性的:MAT(Memory Analyzer Tool)、GChisto等砸捏。

Sun JDK監(jiān)控和故障處理命令有jps jstat jmap jhat jstack jinfo
例子:

JPS:JVM Process Status Tool,顯示指定系統(tǒng)內(nèi)所有的HotSpot虛擬機(jī)進(jìn)程谬运。

$ jps -l -m
  28920 org.apache.catalina.startup.Bootstrap start
  11589 org.apache.catalina.startup.Bootstrap start
  25816 sun.tools.jps.Jps -l -m

jstat(JVM statistics Monitoring)是用于監(jiān)視虛擬機(jī)運(yùn)行時(shí)狀態(tài)信息的命令隙赁,它可以顯示出虛擬機(jī)進(jìn)程中的類裝載、內(nèi)存梆暖、垃圾收集伞访、JIT編譯等運(yùn)行數(shù)據(jù)。

$ jstat -class 11589
 Loaded  Bytes  Unloaded  Bytes     Time   
  7035  14506.3     0     0.0       3.67
  
Loaded : 加載class的數(shù)量
Bytes : class字節(jié)大小
Unloaded : 未加載class的數(shù)量
Bytes : 未加載class的字節(jié)大小
Time : 加載時(shí)間

jmap(JVM Memory Map)命令用于生成heap dump文件轰驳,如果不使用這個(gè)命令厚掷,還闊以使用-XX:+HeapDumpOnOutOfMemoryError參數(shù)來讓虛擬機(jī)出現(xiàn)OOM的時(shí)候·自動(dòng)生成dump文件。 jmap不僅能生成dump文件级解,還闊以查詢finalize執(zhí)行隊(duì)列冒黑、Java堆和永久代的詳細(xì)信息,如當(dāng)前使用率勤哗、當(dāng)前使用的是哪種收集器等抡爹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市芒划,隨后出現(xiàn)的幾起案子冬竟,更是在濱河造成了極大的恐慌,老刑警劉巖民逼,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诱咏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缴挖,警方通過查閱死者的電腦和手機(jī)袋狞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來映屋,“玉大人苟鸯,你說我怎么就攤上這事∨锏悖” “怎么了早处?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瘫析。 經(jīng)常有香客問我砌梆,道長(zhǎng),這世上最難降的妖魔是什么贬循? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任咸包,我火速辦了婚禮,結(jié)果婚禮上杖虾,老公的妹妹穿的比我還像新娘烂瘫。我一直安慰自己,他們只是感情好奇适,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布坟比。 她就那樣靜靜地躺著芦鳍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪葛账。 梳的紋絲不亂的頭發(fā)上柠衅,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音籍琳,去河邊找鬼菲宴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛巩割,可吹牛的內(nèi)容都是我干的裙顽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼宣谈,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼愈犹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起闻丑,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤漩怎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后嗦嗡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勋锤,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年侥祭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叁执。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矮冬,死狀恐怖谈宛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胎署,我是刑警寧澤吆录,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站琼牧,受9級(jí)特大地震影響恢筝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巨坊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一撬槽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抱究,春花似錦恢氯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至妈候,卻和暖如春敢靡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苦银。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工啸胧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幔虏。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓纺念,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親想括。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陷谱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容