九神帶你入門JVM(上)

概述

本篇較長钟病,九神帶你從0入門JVM德挣,全文包括包括JVM的分類彻犁、JVM垃圾回收綜述森瘪、JVM的內(nèi)存模型(Java 8)判哥、對(duì)象存活判斷业舍、GC算法撤缴、常見的GC回收器劫扒、GC日志一共七個(gè)部分捐顷。

下面是一張基于Java 8的JVM思維導(dǎo)圖荡陷,如果需要雨效,請(qǐng)關(guān)注公號(hào):“九神說編程”,回復(fù)“JVM”獲取废赞。

img

JVM的分類

對(duì)于新手徽龟,最大的認(rèn)識(shí)偏差就是覺得只有一種JVM,這個(gè)認(rèn)知是不對(duì)的唉地。SUN公司有對(duì)JVM的規(guī)范据悔,只要符合JVM規(guī)范大家都可以在此基礎(chǔ)上開發(fā)出自己的虛擬機(jī)。事實(shí)上耘沼,我們現(xiàn)在最常用的HotSpot VM就不是SUN公司開發(fā)的极颓。HotSpot VM是對(duì)JVM相關(guān)JSR(Java Specification Requests,Java規(guī)范)的一個(gè)RI(Reference Implementation群嗤,參考實(shí)現(xiàn))菠隆。

關(guān)于相關(guān)的JSR可以在JCP(Java Community Process,Java社區(qū)組織)的官網(wǎng)上尋找狂秘。

如果你想知道你使用的Java虛擬機(jī)的種類骇径,可以在java -version中查詢:

img

1、HotSpot VM

HotSpot VM是絕對(duì)的主流者春。大家用它的時(shí)候很可能就沒想過還有別的選擇既峡,或者是為了遷就依賴了Oracle/Sun JDK某些具體實(shí)現(xiàn)的爛代碼而選擇用HotSpot VM省點(diǎn)心。

Oracle / Sun JDK碧查、OpenJDK的各種變種(例如IcedTea、Zulu)校仑,用的都是相同核心的HotSpot VM忠售。 從Java SE 7開始,HotSpot VM就是Java規(guī)范的“參考實(shí)現(xiàn)”(RI迄沫,Reference Implementation)稻扬。把它叫做“標(biāo)準(zhǔn)JVM”完全不為過。

當(dāng)在面試中問道“Java性能如何如何”羊瘩、“Java有多少種GC”泰佳、“JVM如何調(diào)優(yōu)”等等,默認(rèn)問的就是特指HotSpot VM尘吗,可見其“主流性”逝她。(其實(shí)這不是件好事,可能有些面試官自己都不知道VM需要分類睬捶,當(dāng)我們討論與JVM相關(guān)的問題是還是要具體到哪個(gè)VM才正確嚴(yán)禁)

JDK8的HotSpot VM已經(jīng)是以前的HotSpot VM與JRockit VM的合并版黔宛,也就是傳說中的“HotRockit”,只是產(chǎn)品里名字還是叫HotSpot VM擒贸。這個(gè)合并并不是要把JRockit的部分代碼插進(jìn)HotSpot里臀晃,而是把前者一些有價(jià)值的功能在后者里重新實(shí)現(xiàn)一遍觉渴。移除PermGen、Java Flight Recorder徽惋、jcmd等都屬于合并項(xiàng)目的一部分案淋。

不過要留意的是,這里我說的HotSpot VM特指“正常配置”版险绘,而不包括“Zero / Shark”版踢京。Wikipedia那個(gè)頁面上把后者稱為“Zero Port”。用這個(gè)版本的人應(yīng)該相當(dāng)少隆圆,很多時(shí)候它的release版在其指定OS上都build不成功漱挚!

下面是抄來的一段與之相關(guān)的歷史:

提起HotSpot VM,相信所有Java程序員都知道渺氧,它是Sun JDK和OpenJDK中所帶的虛擬機(jī)旨涝,也是目前使用范圍最廣的Java虛擬機(jī)。 但不一定所有人都知道的是侣背,這個(gè)目前看起來“血統(tǒng)純正”的虛擬機(jī)在最初并非由Sun公司開發(fā)白华,而是由一家名為“Longview Technologies”的小公司設(shè)計(jì)的; 甚至這個(gè)虛擬機(jī)最初并非是為Java語言而開發(fā)的贩耐,它來源于Strongtalk VM弧腥, 而這款虛擬機(jī)中相當(dāng)多的技術(shù)又是來源于一款支持Self語言實(shí)現(xiàn)“達(dá)到C語言50%以上的執(zhí)行效率”的目標(biāo)而設(shè)計(jì)的虛擬機(jī), Sun公司注意到了這款虛擬機(jī)在JIT編譯上有許多優(yōu)秀的理念和實(shí)際效果潮太,在1997年收購了Longview Technologies公司管搪,從而獲得了HotSpot VM。

HotSpot VM既繼承了Sun之前兩款商用虛擬機(jī)的優(yōu)點(diǎn)(如前面提到的準(zhǔn)確式內(nèi)存管理)铡买,也有許多自己新的技術(shù)優(yōu)勢(shì)更鲁, 如它名稱中的HotSpot指的就是它的熱點(diǎn)代碼探測(cè)技術(shù)(其實(shí)兩個(gè)VM基本上是同時(shí)期的獨(dú)立產(chǎn)品,HotSpot還稍早一些奇钞,HotSpot一開始就是準(zhǔn)確式GC澡为, 而Exact VM之中也有與HotSpot幾乎一樣的熱點(diǎn)探測(cè)。 為了Exact VM和HotSpot VM哪個(gè)成為Sun主要支持的VM產(chǎn)品景埃,在Sun公司內(nèi)部還有過爭(zhēng)論媒至,HotSpot打敗Exact并不能算技術(shù)上的勝利), HotSpot VM的熱點(diǎn)代碼探測(cè)能力可以通過執(zhí)行計(jì)數(shù)器找出最具有編譯價(jià)值的代碼谷徙,然后通知JIT編譯器以方法為單位進(jìn)行編譯拒啰。 如果一個(gè)方法被頻繁調(diào)用,或方法中有效循環(huán)次數(shù)很多完慧,將會(huì)分別觸發(fā)標(biāo)準(zhǔn)編譯和OSR(棧上替換)編譯動(dòng)作图呢。 通過編譯器與解釋器恰當(dāng)?shù)貐f(xié)同工作,可以在最優(yōu)化的程序響應(yīng)時(shí)間與最佳執(zhí)行性能中取得平衡,而且無須等待本地代碼輸出才能執(zhí)行程序蛤织, 即時(shí)編譯的時(shí)間壓力也相對(duì)減小赴叹,這樣有助于引入更多的代碼優(yōu)化技術(shù),輸出質(zhì)量更高的本地代碼指蚜。

在2006年的JavaOne大會(huì)上乞巧,Sun公司宣布最終會(huì)把Java開源,并在隨后的一年摊鸡,陸續(xù)將JDK的各個(gè)部分(其中當(dāng)然也包括了HotSpot VM)在GPL協(xié)議下公開了源碼绽媒, 并在此基礎(chǔ)上建立了OpenJDK。這樣免猾,HotSpot VM便成為了Sun JDK和OpenJDK兩個(gè)實(shí)現(xiàn)極度接近的JDK項(xiàng)目的共同虛擬機(jī)是辕。

在2008年和2009年,Oracle公司分別收購了BEA公司和Sun公司猎提,這樣Oracle就同時(shí)擁有了兩款優(yōu)秀的Java虛擬機(jī):JRockit VM和HotSpot VM获三。 Oracle公司宣布在不久的將來(大約應(yīng)在發(fā)布JDK 8的時(shí)候)會(huì)完成這兩款虛擬機(jī)的整合工作,使之優(yōu)勢(shì)互補(bǔ)锨苏。 整合的方式大致上是在HotSpot的基礎(chǔ)上疙教,移植JRockit的優(yōu)秀特性,譬如使用JRockit的垃圾回收器與MissionControl服務(wù)伞租, 使用HotSpot的JIT編譯器與混合的運(yùn)行時(shí)系統(tǒng)贞谓。

2、J9 VM

J9是IBM開發(fā)的一個(gè)高度模塊化的JVM葵诈,這也是我們?cè)诠ぷ髦锌赡軙?huì)用到的另外一個(gè)VM(除了這兩個(gè)裸弦,在中國的大廠里不大可能接觸到其他的了)。

在許多平臺(tái)上作喘,IBM J9 VM都只能跟IBM產(chǎn)品一起使用理疙。這不是技術(shù)限制,而是許可證限制徊都。例如說在Windows上IBM JDK不是免費(fèi)公開的,而是要跟IBM其它產(chǎn)品一起捆綁發(fā)布的广辰;使用IBM Rational暇矫、IBM WebSphere的話都有機(jī)會(huì)用到J9 VM(也可以自己選擇配置使用別的Java SE JVM)。

根據(jù)許可證择吊,這種捆綁在產(chǎn)品里的J9 VM不應(yīng)該用于運(yùn)行別的Java程序李根,但是大家自己“偷偷的”拿來跑別的程序,IBM也沒力氣管几睛。而在一些IBM的硬件平臺(tái)上房轿,很少客戶是只買硬件不買配套軟件的,IBM給一整套解決方案,里面可能就包括了IBM JDK囱持。這樣自然而然就用上了J9 VM夯接。所以J9 VM得算在主流里,雖然很少是大家主動(dòng)選擇的首選纷妆。

J9 VM的性能水平大致跟HotSpot VM是一個(gè)檔次的盔几。有時(shí)HotSpot快些,有時(shí)J9快些掩幢。不過J9 VM有一些HotSpot VM在JDK8還不支持的功能逊拍,最顯著的一個(gè)就是J9支持AOT編譯和更強(qiáng)大的class data sharing。

3际邻、Sun Classic VM

從名字就可以看出來這是SUN公司開發(fā)的第一款商用的虛擬機(jī)芯丧,現(xiàn)在此款虛擬機(jī)已經(jīng)淘汰了。 這個(gè)虛擬機(jī)只能使用純解釋器的方式來執(zhí)行Java代碼世曾。

4缨恒、Exact VM

這是一款被HotSpot VM給PK掉的VM,上面講歷史的時(shí)候講過度硝。它只存在了很短暫的時(shí)間肿轨,而且只在Solaris平臺(tái)發(fā)布過。

5蕊程、JRockit VM

JRockit VM曾經(jīng)號(hào)稱“世界上速度最快的Java虛擬機(jī)” 椒袍。由于專注于服務(wù)器端應(yīng)用,它可以不太關(guān)注程序啟動(dòng)速度藻茂,因此JRockit內(nèi)部不包含解析器實(shí)現(xiàn)驹暑,全部代碼都靠即時(shí) 編譯器編譯后執(zhí)行。

除此之外辨赐,JRockit的垃圾收集器和MissionControl服務(wù)套件等部分的實(shí)現(xiàn)优俘,在眾多Java虛擬機(jī)中也一直處于領(lǐng)先水平。這一套思想已經(jīng)在Java 8中被HotSpot VM給用上了掀序。

6帆焕、其他

其他的VM還有很多很多,比如Dalvik VM不恭、Microsoft JVM叶雹、Azul VM、Liquid VM换吧、Zing VM等等折晦。但是這些VM和我們實(shí)際中不大會(huì)有交集,也不是歷史沾瓦,更不是HotSpot VM曾經(jīng)的競(jìng)品满着,所以這里就不探討了谦炒。

JVM垃圾回收綜述

為了更快搞明白JVM里面的相關(guān)概念,先綜述JVM的垃圾回收(基于Java 8)风喇。

Hotspot VM的垃圾回收采用“分代回收”的算法宁改。“分代回收”是基于這樣一個(gè)事實(shí):對(duì)象的生命周期不同响驴,所以針對(duì)不同生命周期的對(duì)象可以采取不同的回收方式透且,以便提高回收效率。

Hotspot VM將堆劃分為不同的物理區(qū)豁鲤,就是“分代”思想的體現(xiàn)秽誊。如圖所示,JVM堆主要由新生代琳骡、老年代锅论、元空間構(gòu)成。

img

1楣号、新生代(Young Generation):大多數(shù)對(duì)象在新生代中被創(chuàng)建最易,其中很多對(duì)象的生命周期很短。每次新生代的垃圾回收(又稱Minor GC)后只有少量對(duì)象存活炫狱,所以選用復(fù)制算法藻懒,只需要少量的復(fù)制成本就可以完成回收。

新生代內(nèi)又分三個(gè)區(qū):一個(gè)Eden區(qū)视译,一般而言有兩個(gè)Survivor區(qū)嬉荆,大部分對(duì)象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時(shí)酷含,還存活的對(duì)象將被復(fù)制到第一個(gè)Survivor區(qū)鄙早。當(dāng)?shù)谝粋€(gè)Survivor區(qū)滿時(shí),此區(qū)的存活且不滿足“晉升”條件的對(duì)象將被復(fù)制到第一個(gè)Survivor區(qū)椅亚。

對(duì)象每經(jīng)歷一次Minor GC限番,年齡加1,達(dá)到“晉升年齡閾值”后呀舔,被放到老年代弥虐,這個(gè)過程也稱為“晉升”。顯然媚赖,“晉升年齡閾值”的大小直接影響著對(duì)象在新生代中的停留時(shí)間霜瘪,在Serial和ParNew GC兩種回收器中,“晉升年齡閾值”通過參數(shù)MaxTenuringThreshold設(shè)定省古,默認(rèn)值為15粥庄。

2丧失、老年代(Old Generation):在新生代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象豺妓,就會(huì)被放到年老代,該區(qū)域中對(duì)象存活率高。老年代的垃圾回收(又稱Major GC)通常使用“標(biāo)記-清理”或“標(biāo)記-整理”算法琳拭。整堆包括新生代和老年代的垃圾回收稱為Full GC(HotSpot VM里训堆,除了CMS之外,其它能收集老年代的GC都會(huì)同時(shí)收集整個(gè)GC堆白嘁,包括新生代)坑鱼。

3、元空間(Metaspace):主要存放元數(shù)據(jù)絮缅,例如類元信息鲁沥、字段、靜態(tài)屬性耕魄、方法画恰、常量,還有運(yùn)行時(shí)常量池等(不含字符串常量)吸奴,與垃圾回收要回收的Java對(duì)象關(guān)系不大允扇。相對(duì)于新生代和年老代來說,該區(qū)域的劃分對(duì)垃圾回收影響很小则奥。

JVM的內(nèi)存模型(Java 8)

JVM內(nèi)存模型分為堆(heap)考润、元空間、棧读处、本地方法棧糊治、程序計(jì)數(shù)器。

JDK8的內(nèi)存模型如下圖:

img

其中档泽,堆和元空間是線程共享的俊戳,在Java虛擬機(jī)中只有一個(gè)堆、一個(gè)元空間馆匿,并在JVM啟動(dòng)的時(shí)候就創(chuàng)建抑胎,JVM停止才銷毀。棧渐北、本地方法棧阿逃、程序計(jì)數(shù)器是每個(gè)線程私有的,隨著線程的創(chuàng)建而創(chuàng)建赃蛛,隨著線程的結(jié)束而死亡恃锉。

img

1. 本地方法棧

提供虛擬機(jī)使用到的本地Native方法服務(wù)。

2. 程序計(jì)數(shù)器(Program Counter Register)

程序計(jì)數(shù)器是一塊較小的內(nèi)存空間呕臂。寄存器存儲(chǔ)指令相關(guān)的現(xiàn)場(chǎng)信息破托,由于CPU時(shí)間片輪限制,眾多線程在并發(fā)執(zhí)行過程中歧蒋,任何一個(gè)確定的時(shí)刻土砂,一個(gè)處理器或者多核處理器中的一個(gè)內(nèi)核州既,只會(huì)執(zhí)行某個(gè)線程中的一條指令。這樣必然導(dǎo)致經(jīng)常中斷或恢復(fù)萝映,如何保證分毫無差呢?

每個(gè)線程在創(chuàng)建后吴叶,都會(huì)產(chǎn)生自己的程序計(jì)數(shù)器和棧幀,程序計(jì)數(shù)器用來存放執(zhí)行指令的偏移量和行號(hào)指示器等序臂,線程執(zhí)行或恢復(fù)都要依賴程序計(jì)數(shù)器蚌卤。程序計(jì)數(shù)器在各個(gè)線程之間互不影響,此區(qū)域也不會(huì)發(fā)生內(nèi)存溢出異常奥秆。

特點(diǎn):

  • 一塊較小的內(nèi)存空間

  • 線程私有

  • 是唯一一個(gè)不會(huì)出現(xiàn)OOM的內(nèi)存區(qū)域

3. 棧(Stack)

JVM中的虛擬機(jī)棧是描述Java方法執(zhí)行的內(nèi)存區(qū)域逊彭,它是線程私有的。每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀用于存儲(chǔ)局部變量构订、操作數(shù)棧诫龙、動(dòng)態(tài)鏈接、方法出口等信息鲫咽,每一個(gè)方法被調(diào)用直至執(zhí)行完成的過程签赃,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。

如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度分尸,將會(huì)拋出stackoverflowError通常出現(xiàn)在遞歸方法中锦聊;如果虛擬機(jī)可以動(dòng)態(tài)擴(kuò)展,但是無法申請(qǐng)到足夠的內(nèi)存時(shí)箩绍,就會(huì)拋出outOfMemoryError異常孔庭。

img

4、堆

Heap存儲(chǔ)著幾乎所有的對(duì)象及數(shù)組材蛛,JVM8中把靜態(tài)變量(字符串常量池)也移到堆區(qū)進(jìn)行存儲(chǔ)圆到。

堆是OOM故障最主要的發(fā)源地,也是是垃圾回收的主要區(qū)域,所以也被稱為GC堆卑吭。通常情況下芽淡,它占用的空間是所有內(nèi)存區(qū)域中最大的,但如果無節(jié)制地創(chuàng)建大量對(duì)象豆赏,也容易消耗完所有的空間挣菲。堆的內(nèi)存空間既可以固定大小,也可運(yùn)行時(shí)動(dòng)態(tài)地調(diào)整掷邦,通過如下參數(shù)設(shè)定初始值和最大值白胀,比如-Xms256M. -Xmx1024M。其中-X表示它是JVM運(yùn)行參數(shù)抚岗,ms是memorystart初始堆容量的簡(jiǎn)稱 或杠,mx是memory max最大堆容量的簡(jiǎn)稱。但是在通常情況下宣蔚,服務(wù)器在運(yùn)行過程中向抢,堆空間不斷地?cái)U(kuò)容與回縮蔓涧,勢(shì)必形成不必要的系統(tǒng)壓力,所以在線上生產(chǎn)環(huán)境中笋额,JVM的Xms和Xmx設(shè)置成一樣大小,避免在GC后調(diào)整堆大小時(shí)帶來的額外壓力篷扩。

堆分成兩大塊:新生代和老年代兄猩,對(duì)象產(chǎn)生之初在新生代,步入暮年時(shí)進(jìn)入老年代鉴未。新生代又分為1個(gè)Eden區(qū)+ 2個(gè)Survivor區(qū)枢冤,8:1:1的比例。絕大部分對(duì)象在Eden(意為伊甸園)區(qū)生成铜秆,當(dāng)Eden區(qū)裝填滿的時(shí)候淹真,會(huì)觸發(fā)Young GC。垃圾回收的時(shí)候连茧,在Eden區(qū)實(shí)現(xiàn)清除策略核蘸,沒有被引用的對(duì)象則直接回收。依然存活的對(duì)象會(huì)被移送到Survivor(幸存者)區(qū)啸驯,這個(gè)區(qū)真是名副其實(shí)的存在客扎。Survivor 區(qū)分為S0和S1兩塊內(nèi)存空間,送到哪塊空間呢?每次Young GC的時(shí)候罚斗,將存活的對(duì)象復(fù)制到未使用的那塊空間徙鱼,然后將當(dāng)前正在使用的空間完全清除,交換兩塊空間的使用狀態(tài)针姿。

如果Young GC要移送的對(duì)象大于Survivor區(qū)容量上限袱吆,或者超大對(duì)象的閾值超過eden分配擔(dān)保設(shè)置值的上限,則直接移交給老年代.如果老年代也無法放下距淫,則會(huì)觸發(fā)Full Garbage Collection(Full GC)绞绒,如果依然無法放下,則拋OOM.榕暇。

假如一些沒有進(jìn)取心的對(duì)象以為可以一直在新生代的Survivor區(qū)交換來交換去处铛,那就錯(cuò)了。每個(gè)對(duì)象都有一個(gè)計(jì)數(shù)器拐揭,每次Young GC都會(huì)加1撤蟆。-XX:MaxTenuringThreshold參數(shù)能配置計(jì)數(shù)器的值到達(dá)某個(gè)閾值的時(shí)候,對(duì)象從新生代晉升至老年代堂污。默認(rèn)值是15家肯,可以在Survivor 區(qū)交換14次之后,晉升至老年代盟猖。

堆出現(xiàn)OOM的概率是所有內(nèi)存耗盡異常中最高的讨衣。出錯(cuò)時(shí)的堆內(nèi)信息對(duì)解決問題非常有幫助换棚,所以給JVM設(shè)置運(yùn)行參數(shù)-XX:+HeapDumpOnOutOfMemoryError,讓JVM遇到OOM異常時(shí)能輸出堆內(nèi)信息反镇,使用-XX:HeapDumpPath參數(shù)指定dump路徑固蚤。利用JVM參數(shù)-XX:OnOutOfMemoryError可以在發(fā)生OOM異常時(shí),運(yùn)行一個(gè)本機(jī)的腳本或指令歹茶。

img

5夕玩、方法區(qū)和持久代

注意,這部分并不存在與Java8惊豺,屬于Java 8之前的東西燎孟。雖然Java 8移除了這部分,但是這里稍微回顧一下尸昧,可供大家比較不同揩页,也防止萬一面試官同學(xué)根本不懂,還在問方法區(qū)和持久代烹俗。

方法區(qū)中存放已經(jīng)被虛擬機(jī)加載的類信息爆侣、常量、靜態(tài)變量幢妄、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)累提。這部分內(nèi)容中的字符串變量在Java8倍丟到了堆里,其他的部分全部丟入元空間磁浇。

方法區(qū)與堆(Java Heap)一樣斋陪,是各個(gè)線程共享的內(nèi)存區(qū)域。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分置吓,但是它卻有一個(gè)別名叫做非堆(Non-Heap)无虚。他們的區(qū)別是堆存儲(chǔ)對(duì)象數(shù)據(jù),方法區(qū)存儲(chǔ)靜態(tài)信息衍锚。

img

持久代友题,即PermGen space的全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域戴质,有人稱之為永久代度宦。需要知道的是,上述的方法區(qū)是JVM的規(guī)范告匠,持久代是HotSpot VM對(duì)這一規(guī)范的實(shí)現(xiàn)戈抄。不同的Java虛擬機(jī)之間可能會(huì)進(jìn)行類共享,因此持久代又分為只讀區(qū)和讀寫區(qū)后专。

JVM用于描述應(yīng)用程序中用到的類和方法的元數(shù)據(jù)也存儲(chǔ)在持久代中划鸽。JVM運(yùn)行時(shí)會(huì)用到多少持久代的空間取決于應(yīng)用程序用到了多少類。除此之外,Java SE庫中的類和方法也都存儲(chǔ)在這里裸诽。我們把所有存儲(chǔ)的內(nèi)容總結(jié)如下:

  • JVM中類的元數(shù)據(jù)在Java堆中的存儲(chǔ)區(qū)域嫂用。

  • Java類對(duì)應(yīng)的HotSpot虛擬機(jī)中的內(nèi)部表示也存儲(chǔ)在這里。

  • 類的層級(jí)信息丈冬,字段嘱函,名字。

  • 方法的編譯信息及字節(jié)碼埂蕊。

  • 變量

  • 常量池和符號(hào)解析

JVM 種類有很多往弓,需要注意的是,PermGen space是Hotspot才有粒梦,JRockit以及J9是沒有這個(gè)區(qū)域。

6荸实、元空間

隨著JDK8的到來匀们,JVM不再有PermGen。但類的元數(shù)據(jù)信息(metadata)還在准给,只不過不再是存儲(chǔ)在連續(xù)的堆空間上泄朴,而是移動(dòng)到叫做“Metaspace”的本地內(nèi)存(Native memory)中。

img

元空間由Klass Metaspace和NoKlass Mestaspace組成露氮,其中:

  • Klass Metaspace:Klass Metaspace就是用來存klass的祖灰,klass是我們熟知的class文件在jvm里的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),不過有點(diǎn)要提的是我們看到的類似A.class其實(shí)是存在heap里的畔规,是java.lang.Class的一個(gè)對(duì)象實(shí)例局扶。這塊內(nèi)存是緊接著Heap的,和我們之前的perm一樣叁扫,這塊內(nèi)存大小可通過-XX:CompressedClassSpaceSize參數(shù)來控制三妈,這個(gè)參數(shù)默認(rèn)是1G,但是這塊內(nèi)存也可以沒有莫绣,假如沒有開啟壓縮指針就不會(huì)有這塊內(nèi)存畴蒲,這種情況下klass都會(huì)存在NoKlass Metaspace里,另外如果我們把-Xmx設(shè)置大于32G的話对室,其實(shí)也是沒有這塊內(nèi)存的模燥,因?yàn)闀?huì)這么大內(nèi)存會(huì)關(guān)閉壓縮指針開關(guān)。還有就是這塊內(nèi)存最多只會(huì)存在一塊掩宜。

  • NoKlass Metaspace:NoKlass Metaspace專門來存klass相關(guān)的其他的內(nèi)容蔫骂,比如method,constantPool等牺汤,這塊內(nèi)存是由多塊內(nèi)存組合起來的纠吴,所以可以認(rèn)為是不連續(xù)的內(nèi)存塊組成的。這塊內(nèi)存是必須的慧瘤,雖然叫做NoKlass Metaspace戴已,但是也其實(shí)可以存klass的內(nèi)容固该。

Klass Metaspace和NoKlass Mestaspace都是所有classloader共享的,所以類加載器們要分配內(nèi)存糖儡,但是每個(gè)類加載器都有一個(gè)SpaceManager伐坏,來管理屬于這個(gè)類加載的內(nèi)存小塊。如果Klass Metaspace用完了握联,那就會(huì)OOM了桦沉,不過一般情況下不會(huì),NoKlass Mestaspace是由一塊塊內(nèi)存慢慢組合起來的金闽,在沒有達(dá)到限制條件的情況下纯露,會(huì)不斷加長這條鏈,讓它可以持續(xù)工作代芜。

元空間的本質(zhì)是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)埠褪。不過元空間與持久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存挤庇。因此钞速,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制嫡秕,但可以通過以下參數(shù)來指定元空間的大锌视铩:

  • -XX:MetaspaceSize,初始空間大小昆咽,達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類型卸載驾凶,同時(shí)GC會(huì)對(duì)該值進(jìn)行調(diào)整:如果釋放了大量的空間,就適當(dāng)降低該值掷酗;如果釋放了很少的空間狭郑,那么在不超過MaxMetaspaceSize時(shí),適當(dāng)提高該值汇在。

  • -XX:MaxMetaspaceSize翰萨,最大空間,默認(rèn)是沒有限制的糕殉。

除了上面兩個(gè)指定大小的選項(xiàng)以外亩鬼,還有兩個(gè)與 GC 相關(guān)的屬性:

  • -XX:MinMetaspaceFreeRatio,在GC之后阿蝶,最小的Metaspace剩余空間容量的百分比雳锋,減少為分配空間所導(dǎo)致的垃圾收集

  • -XX:MaxMetaspaceFreeRatio,在GC之后羡洁,最大的Metaspace剩余空間容量的百分比玷过,減少為釋放空間所導(dǎo)致的垃圾收集

下面講一下元空間的特點(diǎn):

  • 充分利用了Java語言規(guī)范中的好處:類及相關(guān)的元數(shù)據(jù)的生命周期與類加載器的一致。

  • 每個(gè)加載器有專門的存儲(chǔ)空間

  • 只進(jìn)行線性分配

  • 不會(huì)單獨(dú)回收某個(gè)類

  • 省掉了GC掃描及壓縮的時(shí)間

  • 元空間里的對(duì)象的位置是固定的

  • 如果GC發(fā)現(xiàn)某個(gè)類加載器不再存活了,會(huì)把相關(guān)的空間整個(gè)回收掉

最后講一下元空間的內(nèi)存分配模型:

  • 絕大多數(shù)的類元數(shù)據(jù)的空間都從本地內(nèi)存中分配

  • 用來描述類元數(shù)據(jù)的類(klasses)也被刪除了

  • 分元數(shù)據(jù)分配了多個(gè)虛擬內(nèi)存空間

  • 給每個(gè)類加載器分配一個(gè)內(nèi)存塊的列表辛蚊。塊的大小取決于類加載器的類型; sun/反射/代理對(duì)應(yīng)的類加載器的塊會(huì)小一些

  • 歸還內(nèi)存塊粤蝎,釋放內(nèi)存塊列表

  • 一旦元空間的數(shù)據(jù)被清空了,虛擬內(nèi)存的空間會(huì)被回收掉

  • 減少碎片的策略

7袋马、為什么移除持久代

對(duì)于老的Java程序員初澎,我們都會(huì)遇到一個(gè)異常:java.lang.OutOfMemoryError: PermGen space 。說白了就是持久代內(nèi)存溢出虑凛!

為什么會(huì)內(nèi)存溢出呢碑宴?因?yàn)槌志么糜诖娣臗lass和Meta的信息,Class在被 Load的時(shí)候被放入PermGen space區(qū)域桑谍,它和和存放Instance的Heap區(qū)域不同延柠,所以如果你的APP會(huì)LOAD很多CLASS的話,就很可能出現(xiàn)PermGen space錯(cuò)誤。這種錯(cuò)誤常見在web服務(wù)器對(duì)JSP進(jìn)行pre compile的時(shí)候锣披。

而這個(gè)異常實(shí)質(zhì)是暴露了這一設(shè)計(jì)根源的一個(gè)問題:

持久代大小受到-XX:PermSize和-XX:MaxPermSize兩個(gè)參數(shù)的限制贞间,而這兩個(gè)參數(shù)又受到JVM設(shè)定的內(nèi)存大小限制,這就導(dǎo)致在使用中可能會(huì)出現(xiàn)持久代內(nèi)存溢出的問題盈罐。

另外一方面榜跌,為了和JRockit進(jìn)行融合而做的努力闪唆,JRockit用戶并不需要配置持久代盅粪,所以HotSpot也移除了持久代。

根據(jù)上面的內(nèi)外兩方面原因悄蕾,持久代最終被移除票顾,方法區(qū)移至Metaspace,字符串常量移至Java Heap帆调。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奠骄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子番刊,更是在濱河造成了極大的恐慌含鳞,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芹务,死亡現(xiàn)場(chǎng)離奇詭異蝉绷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)枣抱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門熔吗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人佳晶,你說我怎么就攤上這事桅狠。” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵中跌,是天一觀的道長咨堤。 經(jīng)常有香客問我,道長晒他,這世上最難降的妖魔是什么吱型? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮陨仅,結(jié)果婚禮上津滞,老公的妹妹穿的比我還像新娘。我一直安慰自己灼伤,他們只是感情好触徐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狐赡,像睡著了一般撞鹉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颖侄,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天鸟雏,我揣著相機(jī)與錄音,去河邊找鬼览祖。 笑死孝鹊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的展蒂。 我是一名探鬼主播又活,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼锰悼!你這毒婦竟也來了柳骄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤箕般,失蹤者是張志新(化名)和其女友劉穎耐薯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丝里,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曲初,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丙者。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片复斥。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖械媒,靈堂內(nèi)的尸體忽然破棺而出目锭,到底是詐尸還是另有隱情评汰,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布痢虹,位于F島的核電站被去,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏奖唯。R本人自食惡果不足惜惨缆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丰捷。 院中可真熱鬧坯墨,春花似錦、人聲如沸病往。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽停巷。三九已至耍攘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間畔勤,已是汗流浹背蕾各。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庆揪,地道東北人式曲。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像嚷硫,于是被迫代替她去往敵國和親检访。 傳聞我的和親對(duì)象是個(gè)殘疾皇子始鱼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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