JVM基礎(chǔ)知識

引言:JVM常見面試題:

1艾船、請談?wù)勀銓VM的理解?java8的虛擬機(jī)有什么更新高每?
2屿岂、什么是OOM?什么是StackOverflowError鲸匿?有哪些方法分析爷怀?
3、JVM的常用調(diào)優(yōu)參數(shù)你知道哪些带欢?
4运授、談?wù)凧VM中,對類加載器你的認(rèn)識乔煞?
5吁朦、JVM內(nèi)存模型以及分區(qū),需要詳細(xì)到每個區(qū)放什么
6渡贾、堆里面的分區(qū):Eden逗宜、survival from to、老年代各自的特點
7空骚、GC的三種收集算法:標(biāo)記清除纺讲、標(biāo)記整理、復(fù)制算法的原理與特點囤屹,分別用在什么地方
8熬甚、Minor GC與Full GC分別在什么時候發(fā)生

JVM體系結(jié)構(gòu)概覽

一、類裝載器(ClassLoader):

負(fù)責(zé)加載class文件肋坚,class文件在文件開頭有特定的文件標(biāo)識则涯,將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中复局,并將這些內(nèi)容轉(zhuǎn)換成方法區(qū)中的運行時數(shù)據(jù)結(jié)構(gòu)并且ClassLoader只負(fù)責(zé)class文件的加載,至于它是否可以運行粟判,則由Execution Engine決定

類裝載器

啟動類加載器加載的是java程序最底層的一下jar包亿昏,放在rt.jar,java的版本在不斷的更迭档礁,新的jar包由擴(kuò)展類加載器進(jìn)行加載角钩。應(yīng)用程序類加載器則是加載我們在程序中自己寫的類。

幾種類加載器
雙親委派機(jī)制
public class Obb {
    public static void main(String[] args) {
        Object o = new Object();
        System.out.println("o:" + o.getClass().getClassLoader());
        Obb ob = new Obb();
        System.out.println("ob:" + ob.getClass().getClassLoader());
    }
}

如上所示一段代碼呻澜,Object是jdk自帶的递礼,所以它的類加載器應(yīng)該是根加載器,而ob是我自己定義的羹幸,他的加載器理所當(dāng)然的應(yīng)該是應(yīng)用程序類加載器脊髓,根加載器返回的是null。

示例

二栅受、本地方法棧和本地方法接口

本地方法棧和本地方法接口
線程start方法的底層實際上是native接口

三将硝、程序計數(shù)器

程序計數(shù)器

四、方法區(qū)(Method Area):

供各線程共享時的內(nèi)存區(qū)域屏镊。它存儲了每一個類的結(jié)構(gòu)信息依疼,例如運行時常量池、字段和方法數(shù)據(jù)而芥、構(gòu)造函數(shù)和普通方法的字節(jié)碼內(nèi)容律罢。上面講的時規(guī)范,在不同虛擬機(jī)里頭實現(xiàn)時不一樣的棍丐,最典型的就是永久代(java8以前)和元空間(java8以后)误辑。
實例變量時存放在堆內(nèi)存的,和方法區(qū)無關(guān)歌逢。

五巾钉、棧(Stack):

棧也叫棧內(nèi)存,主管java程序的運行趋翻,是在線程創(chuàng)建時創(chuàng)建,它的生命周期是跟隨線程的生命周期盒蟆,線程結(jié)束棧內(nèi)存也就釋放踏烙,對于棧來說不存在垃圾回收問題,只要線程一結(jié)束历等,該棧就Over讨惩,生命周期和線程一致,是線程私有的寒屯。8種基本類型的變量+對象的引用變量+實例方法都是在函數(shù)的棧內(nèi)存種分配荐捻。

棧管運行黍少,堆管存儲。

棧存儲什么处面?

棧幀種主要保存三類數(shù)據(jù):
本地變量:輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量厂置;
棧操作:記錄出棧,入棧的操作魂角;
棧幀數(shù)據(jù):包括類文件昵济、方法等等。

棧運行原理
棧運行示例圖

/**
 * 如下所示野揪,main方法作為程序的入口访忿,首先被作為棧幀壓入棧的底部,
 * 然后main方法又調(diào)用了sayHello方法斯稳,將此方法棧幀壓入棧中海铆,
 * 而sayHello方法又不停的自己調(diào)用,每次調(diào)用方法都會形成一個棧幀壓入棧挣惰,
 * 而棧的大小是有限的卧斟,所以最后會導(dǎo)致棧溢出  StackOverflowError
 *注意StackOverflowError是錯誤而不是異常
 */
public class StackDemo1 {

    public static void main(String[] args) {
        System.out.println("1");
        sayHello();
        System.out.println("3");
    }

    public static void sayHello() {
        System.out.println("hello");
        sayHello();
    }
}
棧、堆通熄、方法區(qū)的關(guān)系

在棧中存著對象的引用唆涝,指向堆中,在棧中每一個引用存儲著的是對象的地址唇辨,都會在堆中對應(yīng)著一個類元數(shù)據(jù)地址廊酣,而堆中的類元數(shù)據(jù)地址會指向方法區(qū),因為方法區(qū)存儲著這個對象的結(jié)構(gòu)數(shù)據(jù)赏枚,也就是一個對象的模板亡驰。

六、堆(Heap):

一個JVM實例只存在一個堆內(nèi)存饿幅,堆內(nèi)存的大小是可以調(diào)節(jié)的凡辱,類加載器讀取了類文件后,需要把類栗恩、方法透乾、常變量放到堆內(nèi)存中,保存所有引用類型的真實信息磕秤,以方便執(zhí)行器執(zhí)行乳乌。堆內(nèi)存分為三部分:

堆內(nèi)存的邏輯分區(qū)

堆內(nèi)存的邏輯分區(qū)

PS:
幸存者0區(qū)又稱為S0區(qū)/From區(qū)
幸存者1區(qū)又稱為S1區(qū)/To區(qū)

邏輯內(nèi)存上看

java堆從GC的角度上還可以細(xì)分為:新生代(Eden區(qū)、From Survivor區(qū)和To Survivor區(qū))和老年代市咆。

物理內(nèi)存占比
MinorGC的過程
永久帶也就是java8的元空間

元空間是一個常駐內(nèi)存區(qū)域汉操,用于存放JDK自身所攜帶的Class,Interface的元數(shù)據(jù)蒙兰,也就是說它存儲的是運行環(huán)境必須的類信息磷瘤,被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會被垃圾回收器回收掉的芒篷,關(guān)閉JVM才會釋放此區(qū)域所占用的內(nèi)存。

七采缚、JVM的參數(shù)調(diào)節(jié)

java7的JVM
java8的JVM

如上圖所見针炉,java7和java8對于jvm參數(shù)的調(diào)整,實際上相差不大仰担,
-Xms表示堆內(nèi)存的起始值糊识,默認(rèn)是物理內(nèi)存的1/64
-Xmx表示堆內(nèi)存的最大值,默認(rèn)是物理內(nèi)存的1/4
-Xmn表示堆中young區(qū)設(shè)置的值

默認(rèn)空余堆內(nèi)存小于40%時摔蓝,JVM就會增大堆直到-Xmx的最大限制赂苗;
空余堆內(nèi)存大于70%時,JVM會減少堆直到 -Xms的最小限制贮尉。
因此服務(wù)器一般設(shè)置-Xms拌滋、-Xmx相等以避免在每次GC 后調(diào)整堆的大小。對象的堆內(nèi)存由稱為垃圾回收器的自動內(nèi)存管理系統(tǒng)回收猜谚。

(java8以前的永久代)
-XX:PermSize設(shè)置非堆內(nèi)存初始值败砂,默認(rèn)是物理內(nèi)存的1/64;
XX:MaxPermSize設(shè)置最大非堆內(nèi)存的大小魏铅,默認(rèn)是物理內(nèi)存的1/4昌犹。

在java8以后,永久代已經(jīng)被移除览芳,被元空間所代替斜姥,元空間本質(zhì)上和永久代類似。區(qū)別在于永久代使用的是JVM的堆內(nèi)存沧竟,但是java8以后元空間并不在虛擬機(jī)中而是使用本機(jī)物理內(nèi)存铸敏。因此,默認(rèn)情況下悟泵,元空間的大小受本地內(nèi)存限制杈笔。類的元數(shù)據(jù)放入native memory,字符串池和類的靜態(tài)變量放入java堆中糕非,這樣可以加載多少類的元數(shù)據(jù)就不再由maxPerSize控制蒙具,而是由系統(tǒng)的實際可用空間來控制。

堆調(diào)優(yōu)簡介
public class jvmSwitch {
    public static void main(String[] args) {
        //返回JVM試圖使用的最大內(nèi)存量朽肥,默認(rèn)的是系統(tǒng)內(nèi)存的1/4左右  3616MB/16G
        long maxMemory = Runtime.getRuntime().maxMemory();
        //返回JVM的內(nèi)存總量
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("MAX_MEMORY=" + maxMemory + "(字節(jié))" + 
                (maxMemory / 1024 / 1024) + "MB");
        System.out.println("TOTAL_MEMORY=" + totalMemory + "(字節(jié))" + 
                (totalMemory / 1024 / 1024) + "MB");
    }
}
IDEA調(diào)節(jié)JVM參數(shù)
IDEA調(diào)節(jié)JVM參數(shù)
GC收集的詳細(xì)信息
先把最大內(nèi)存改成了10MB
OutOfMemoryError
OOM
讀取MinorGC日志

八:GC算法

分代收集算法=ぁ!鞠呈!

1融师、GC是分代收集算法:次數(shù)上頻繁收集的是Young區(qū)右钾、次數(shù)上較少收集的是old區(qū)蚁吝,基本不動的是元空間旱爆。

總體概述

JVM在進(jìn)行GC時,并非每次都對三個內(nèi)存一起回收的窘茁,大部分時候回收的都是新生代怀伦,因此GC按照回收的區(qū)域又分了兩種類型,一種是普通GC(minor GC)山林,一種時全局GC(major GC or Full GC)

Minor GC和Full GC的區(qū)別:
minorGC:只針對新生代區(qū)域的GC房待,指發(fā)生在新生代的垃圾收集動作,因為大多數(shù)Java對象存活率都不高驼抹,所以Minor GC非常頻繁桑孩,一般回收速度也比較快。
majorGC:指發(fā)生在老年帶的垃圾收集動作框冀,出現(xiàn)了Major GC流椒,經(jīng)常會伴隨至少一次的Minor GC(但是不是絕對的),Major GC的速度一般要比Minor GC慢上10倍以上(因為老年代比新生代大懊饕病P骸)。

四大算法:引用計數(shù)法温数、復(fù)制算法绣硝、標(biāo)記清除法、標(biāo)記壓縮法

2撑刺、引用計數(shù)法

引用計數(shù)法

引用計數(shù)法如上所示鹉胖,在程序中有一個引用,就+1猜煮,但是可能會出現(xiàn)A引用B次员,B引用A的情況,這樣的話循環(huán)引用較難處理王带。

2淑蔚、復(fù)制算法

年輕代中的GC,主要靠的是復(fù)制算法愕撰,年輕代中分為一個Eden和兩個Survivor,默認(rèn)比例是8:1:1刹衫。一般情況下新創(chuàng)建的對象會被分配到Eden區(qū)(一些大的對象特殊處理)。因為年輕代中的對象基本上都是朝生夕死搞挣,所以年輕代的垃圾回收算法使用的是復(fù)制算法带迟,復(fù)制算法的思想就是將內(nèi)存分為兩塊,每次用其中一塊囱桨,當(dāng)這一塊內(nèi)存用完仓犬,就將還活著的對象復(fù)制到另一塊上面,復(fù)制算法不會產(chǎn)生內(nèi)存碎片舍肠。

原理

在GC開始的時候,對象只會存在于Eden區(qū)和名為"From"的Survivor區(qū)财边,Survivor區(qū)"To"是空的酣难。緊接著進(jìn)行GC飞主,Eden區(qū)中所有存活的對象都會被復(fù)制到"To",而在“From”區(qū)中吵瞻,仍會存活的對象就會根據(jù)他們的年齡值來決定去向眯停。年齡達(dá)到一定值(年齡閾值莺债,可以通過-XX:MaxTenuringThreshold來設(shè)置)的對象會被移動到老年代签夭,沒有達(dá)到閾值的對象會被復(fù)制到"To"區(qū)域第租。經(jīng)過這次"From",新的"From"就是上次GC前的"To"丐吓,不管怎樣趟据,都會保證名為To的Survivor區(qū)域是空的汹碱。Minor GC會一直重復(fù)這樣的過程,直到“To”區(qū)域被填滿稚新,"To"區(qū)被填滿之后,會將所有對象移動到老年代中笤妙。

缺點:
1、浪費了一半的內(nèi)存
2召衔、如果對象存活率高苍凛,可以極端一點醇蝴,假設(shè)是100%存活想罕,那么我們需要將所有的對象都復(fù)制一遍悠栓,并將所有引用地址重置一遍按价,復(fù)制這一工作所花費的時間,在對象存活率達(dá)到一定程度時楼镐,將會變得不可忽視癞志。所以要使用復(fù)制算法框产,存活率必須非常低才行今阳,更重要的是,要克服50%內(nèi)存的浪費盾舌。

3蘸鲸、標(biāo)記清除算法

image.png

1膝舅、優(yōu)缺點
優(yōu)點:不需要額外的空間
缺點:兩次掃描仍稀,耗時嚴(yán)重;會產(chǎn)生內(nèi)存碎片。

當(dāng)程序運行時享幽,若可以使用的內(nèi)存被耗盡的時候铲掐,GC線程就會被觸發(fā)并將程序暫停,隨后將要回收的對象標(biāo)記一遍值桩,最終統(tǒng)一回收這些對象摆霉,完成標(biāo)記清理工作接下來便讓應(yīng)用程序恢復(fù)運行。

缺點解釋:首先奔坟,他的缺點就是效率比較低(遞歸與全堆對象遍歷)携栋,而且在進(jìn)行GC的時候,需要停止應(yīng)用程序咳秉,這會導(dǎo)致用戶體驗非常差勁刻两。其次,主要缺點則是這種方式清理出來的空間內(nèi)存事不連續(xù)的滴某,因為死亡對象都是隨機(jī)出現(xiàn)在內(nèi)存的各個角落的磅摹,現(xiàn)在把它們清除之后,內(nèi)存的布局就會亂七八糟霎奢。為了應(yīng)對這一點户誓,JVM必須維持一個內(nèi)存的空間列表,這又是一種開銷幕侠,而且在分配數(shù)組對象的時候帝美,尋找連續(xù)的內(nèi)存空間會不太好找。

4晤硕、標(biāo)記整理法/標(biāo)記壓縮法(全稱:標(biāo)記-清除-整理算法)

老年代一般是由標(biāo)記清除或者是標(biāo)記清除+標(biāo)記整理

原理

優(yōu)缺點:
優(yōu)點:沒有內(nèi)存碎片
缺點:需要移動對象的成本(耗時長)

效率不高悼潭,不僅要標(biāo)記所有存活對象,還要整理所有存活對象的引用地址舞箍,從效率上來講舰褪,標(biāo)記/整理算法要低于復(fù)制算法。

5疏橄、總結(jié):
內(nèi)存效率:復(fù)制算法>標(biāo)記清除算法>標(biāo)記整理算法(此處的效率只是簡單的對比時間復(fù)雜度占拍,實際情況不一定如此)
內(nèi)存整齊度:復(fù)制算法=標(biāo)記整理算法>標(biāo)記清除算法略就。
內(nèi)存利用率:標(biāo)記整理算法=標(biāo)記清除算法>復(fù)制算法

可以看出效率上來講,復(fù)制算法是最快的晃酒,但是浪費了太多的內(nèi)存表牢,而為了盡量兼顧上面所提到的三個指標(biāo),標(biāo)記/整理算法相對來說更平滑一些贝次,但效率上依然不盡如人意崔兴,它比復(fù)制算法多了一個標(biāo)記的階段,又比標(biāo)記/清除多了一個整理內(nèi)存的過程

沒有最優(yōu)的算法蛔翅,只有最合適的算法敲茄。======>>>>分代收集算法

年輕代
年輕代的特點是區(qū)域相對老年代較小,對象存活率低搁宾。這種情況用復(fù)制算法回收整理,速度是最快的倔幼。
老年代
這種情況存在大量存活率高的對象盖腿,復(fù)制算法變得不合適,一般是由標(biāo)記清除與標(biāo)記整理的混合實現(xiàn)损同。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翩腐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子膏燃,更是在濱河造成了極大的恐慌茂卦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件组哩,死亡現(xiàn)場離奇詭異等龙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)伶贰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門蛛砰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人黍衙,你說我怎么就攤上這事泥畅。” “怎么了琅翻?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵位仁,是天一觀的道長。 經(jīng)常有香客問我方椎,道長聂抢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任棠众,我火速辦了婚禮涛浙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己轿亮,他們只是感情好疮薇,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著我注,像睡著了一般按咒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上但骨,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天励七,我揣著相機(jī)與錄音,去河邊找鬼奔缠。 笑死掠抬,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的校哎。 我是一名探鬼主播两波,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼闷哆!你這毒婦竟也來了腰奋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤抱怔,失蹤者是張志新(化名)和其女友劉穎劣坊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屈留,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡局冰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了灌危。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锐想。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖乍狐,靈堂內(nèi)的尸體忽然破棺而出赠摇,到底是詐尸還是另有隱情,我是刑警寧澤浅蚪,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布藕帜,位于F島的核電站,受9級特大地震影響惜傲,放射性物質(zhì)發(fā)生泄漏洽故。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一盗誊、第九天 我趴在偏房一處隱蔽的房頂上張望时甚。 院中可真熱鬧隘弊,春花似錦、人聲如沸荒适。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刀诬。三九已至咽扇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陕壹,已是汗流浹背质欲。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留糠馆,地道東北人嘶伟。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像又碌,于是被迫代替她去往敵國和親九昧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354