深入學(xué)習(xí)JVM 【1】運(yùn)行時(shí)數(shù)據(jù)區(qū)域劃分

前言

在使用c++進(jìn)行編程時(shí)淘衙,我們通過(guò)new創(chuàng)建的每一個(gè)對(duì)象都需要有對(duì)應(yīng)的delete操作去釋放對(duì)象所占用的內(nèi)存横浑,對(duì)內(nèi)存的掌控度比較高剔桨,但是程序員需要知道對(duì)象什么時(shí)候不需要使用了,并需要手動(dòng)釋放內(nèi)存徙融,如果忘記了delete釋放领炫,很容易出現(xiàn)內(nèi)存泄漏(申請(qǐng)內(nèi)存后,沒(méi)有釋放张咳,會(huì)一直占用著)和內(nèi)存溢出(因?yàn)檫^(guò)多的內(nèi)存泄漏導(dǎo)致無(wú)法申請(qǐng)足夠的內(nèi)存帝洪,即out of memory)的問(wèn)題。

相比之下脚猾,java虛擬機(jī)提供了自動(dòng)內(nèi)存管理機(jī)制葱峡,java程序員可以解放雙手,不再需要去寫(xiě)delete等手動(dòng)釋放內(nèi)存的代碼龙助,虛擬機(jī)會(huì)自動(dòng)將內(nèi)存中無(wú)用的對(duì)象占用的內(nèi)存釋放砰奕。

了解jvm的必要

雖然有自動(dòng)內(nèi)存管理機(jī)制的存在蛛芥,但是不代表寫(xiě)的每個(gè)java程序都不存在內(nèi)存泄漏和內(nèi)存溢出問(wèn)題,我們需要對(duì)虛擬機(jī)有足夠的了解军援,才能在發(fā)生內(nèi)存泄露和內(nèi)存溢出的時(shí)候有效地排查問(wèn)題仅淑。

本文將對(duì)jvm虛擬機(jī)運(yùn)行時(shí)內(nèi)存進(jìn)行一個(gè)基本的介紹,后續(xù)的文章也會(huì)講解jvm其他知識(shí)胸哥,大部分都是自己的讀書(shū)總結(jié)加上自己的理解涯竟。希望將自己的所學(xué)進(jìn)行總結(jié)的同時(shí)能惠及他人,如果有什么地方講的不對(duì)空厌,希望各位同學(xué)能夠指出庐船。

內(nèi)存劃分

java虛擬機(jī)將其管理的內(nèi)存劃分為以下幾塊:

  • 程序計(jì)數(shù)器 (PC Register)
  • 虛擬機(jī)棧 (JVM Stack)
  • 本地方法棧 (Native Method Stack)
  • 堆 (Heap)
  • 方法區(qū) (Method Area)
運(yùn)行時(shí)數(shù)據(jù)區(qū)域.png

各個(gè)區(qū)域都有其各自的特點(diǎn)和作用,以及不同的創(chuàng)建和銷(xiāo)毀的時(shí)間

各個(gè)區(qū)域的介紹

程序計(jì)數(shù)器

  • 描述
    • 程序計(jì)數(shù)器是一個(gè)較小的內(nèi)存區(qū)域
  • 作用
    • 記錄著當(dāng)前線程所執(zhí)行的字節(jié)碼行號(hào)嘲更。
    • 字節(jié)碼解釋器在工作的時(shí)候筐钟,通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條要執(zhí)行的字節(jié)碼指令。
    • 分支赋朦,循環(huán)篓冲,跳轉(zhuǎn)異常處理宠哄,線程恢復(fù)等功能都需要使用到這個(gè)程序計(jì)數(shù)器纹因。
  • 特點(diǎn)
    • 線程私有--每個(gè)線程都有一個(gè)獨(dú)立的程序計(jì)數(shù)器。
    • 如果當(dāng)前線程正在執(zhí)行一個(gè)java方法琳拨,這程序計(jì)數(shù)器的值為虛擬機(jī)字節(jié)碼指令的地址瞭恰,如果執(zhí)行的是一個(gè)Native 方法,這個(gè)計(jì)數(shù)器的值則為空狱庇。
    • 程序計(jì)數(shù)器是唯一沒(méi)有規(guī)定OutOfMemoryError的內(nèi)存區(qū)域惊畏。
  • 創(chuàng)建時(shí)間
    • 每個(gè)線程啟動(dòng)的時(shí)候會(huì)創(chuàng)建一個(gè)較小的內(nèi)存區(qū)域作為線程的程序計(jì)數(shù)器
  • 銷(xiāo)毀時(shí)間
    • 線程結(jié)束時(shí)會(huì)釋放該內(nèi)存區(qū)域

擴(kuò)展問(wèn)題1:為什么需要程序計(jì)數(shù)器?

java虛擬機(jī)的多線程是通過(guò)線程輪轉(zhuǎn)密任,分配CPU時(shí)間片來(lái)執(zhí)行java程序颜启,當(dāng)線程切換時(shí),為了能夠回到原來(lái)的字節(jié)碼執(zhí)行位置繼續(xù)程序的執(zhí)行浪讳,所以每個(gè)線程會(huì)有一個(gè)程序計(jì)數(shù)器缰盏。

擴(kuò)展問(wèn)題2:Native方法是什么?

java程序執(zhí)行的時(shí)候調(diào)用的方法淹遵,有些是用java語(yǔ)言實(shí)現(xiàn)的口猜,有些是用其他語(yǔ)言編寫(xiě)實(shí)現(xiàn)的,用其他語(yǔ)言實(shí)現(xiàn)的方法稱(chēng)為Native方法本地方法,native方法會(huì)使用native關(guān)鍵字進(jìn)行標(biāo)注,如Object類(lèi)的getClass()方法:

public class Object {
    
    public final native Class<?> getClass();
    ...
}

由于native方法不是java實(shí)現(xiàn)的透揣,也就沒(méi)有字節(jié)碼行號(hào)之說(shuō)济炎,此時(shí)程序計(jì)數(shù)器的值應(yīng)當(dāng)為空(undefined)。

虛擬機(jī)棧

  • 描述
    • 虛擬機(jī)棧是描述java方法執(zhí)行過(guò)程的一個(gè)內(nèi)存模型辐真。
    • 具體描述:每個(gè)方法在執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀须尚,棧幀中存儲(chǔ)的是java方法的局部變量表崖堤、操作數(shù)棧、動(dòng)態(tài)鏈接耐床、方法出口等信息密幔。java程序在執(zhí)行的時(shí)候每調(diào)用一個(gè)java方法都會(huì)對(duì)應(yīng)的創(chuàng)建棧幀并壓入虛擬機(jī)棧中,當(dāng)方法執(zhí)行完畢撩轰,又會(huì)將棧幀從虛擬機(jī)棧中彈出胯甩。虛擬機(jī)棧就是棧幀存放的一個(gè)棧結(jié)構(gòu)的內(nèi)存區(qū)域。
  • 作用
    • 描述java方法執(zhí)行的過(guò)程钧敞,保存棧幀蜡豹。
  • 特點(diǎn)
    • 線程私有
    • 此區(qū)域可能會(huì)有兩種內(nèi)存異常情況:
      • 當(dāng)棧的深度大于虛擬機(jī)所限制的最大深度麸粮,會(huì)拋出StackOverflowError異常溉苛。
      • 如果虛擬機(jī)棧動(dòng)態(tài)擴(kuò)展無(wú)法申請(qǐng)到足夠的內(nèi)存,就會(huì)拋出OutOfMemoryError異常弄诲。
  • 創(chuàng)建時(shí)間
    • 線程啟動(dòng)的時(shí)候
  • 銷(xiāo)毀時(shí)間
    • 線程結(jié)束的時(shí)候

擴(kuò)展1:局部變量表

局部變量表用于存放編譯期可知的各種基本數(shù)據(jù)類(lèi)型愚战、對(duì)象引用、returnAddress類(lèi)型(一條字節(jié)碼指令的地址)

對(duì)于基本數(shù)據(jù)類(lèi)型,存放的是變量的名和值悄但;

對(duì)于引用類(lèi)型澜薄,存放的是指向?qū)ο笤诙阎械钠鹗嫉刂贰?/p>

ps: 對(duì)于64位的long或double類(lèi)型的局部變量會(huì)占用兩個(gè)局部變量表空間(Slot),其余的數(shù)據(jù)類(lèi)型都是只占用一個(gè)局部變量表空間。

局部變量表所需要的空間在編譯期間已經(jīng)計(jì)算好了佃迄,在一個(gè)方法執(zhí)行時(shí),需要為棧幀分配多少局部變量表空間是完全確定的

本地方法棧

本地方法棧的特性和虛擬機(jī)棧幾乎一樣断序。

  • 本地方法棧與虛擬機(jī)棧的區(qū)別
    • 本地方法棧為本地方法服務(wù)
  • 本地方法棧可能出現(xiàn)的異常
    • 同虛擬機(jī)棧一樣可能拋出StackOverflowErrorOutOfMemoryError 異常糜烹。

  • 描述
    • 堆內(nèi)存的唯一目的是存放對(duì)象實(shí)例违诗。
    • 堆內(nèi)存是垃圾收集的主要區(qū)域,因此也叫GC堆
  • 作用
    • 存放對(duì)象實(shí)例
  • 特點(diǎn)
    • 虛擬機(jī)所管理的內(nèi)存中最大的一塊
    • 幾乎所有的對(duì)象都在堆區(qū)分配內(nèi)存疮蹦,當(dāng)然也有例外诸迟,JIT編譯器有可能會(huì)進(jìn)行優(yōu)化,直接在棧上分配愕乎,有關(guān)信息可以直接搜索“逃逸分析”了解阵苇,這不在本文的討論范圍內(nèi)。
    • 所有線程共享的一塊內(nèi)存區(qū)域
    • 堆內(nèi)存在物理上不一定是連續(xù)的感论,保證邏輯連續(xù)即可
    • 堆內(nèi)存區(qū)域無(wú)法滿足分配對(duì)象實(shí)例所需內(nèi)存慎玖,可能拋出OutOfMemoryError異常
    • 堆內(nèi)存設(shè)置固定大小也可以動(dòng)態(tài)擴(kuò)展,可在啟動(dòng)參數(shù)上指定最小大小及擴(kuò)容的上限笛粘。
  • 創(chuàng)建時(shí)間
    • 虛擬機(jī)啟動(dòng)的時(shí)候就創(chuàng)建了堆內(nèi)存

擴(kuò)展1: 堆區(qū)細(xì)分

jvm為了垃圾回收的方便趁怔,將堆劃分為新生代老年代,新創(chuàng)建的對(duì)象基本上都放在新生代中湿硝,而存活比較久的對(duì)象則會(huì)移到老年代中。新生代和老年代采用不同的垃圾收集算法润努,可以更高效地回收內(nèi)存关斜。采用復(fù)制算法的新生代還可以細(xì)分為EdenFrom SurvivorTo Survivor铺浇。具體的詳情是怎樣的痢畜,為了不偏離這篇文章的主旨,這里先打個(gè)問(wèn)號(hào)鳍侣,后序的文章將會(huì)詳細(xì)介紹堆區(qū)的幾個(gè)劃分的用途丁稀。

堆區(qū)雖然是線程共享的,但是如果設(shè)定了啟動(dòng)參數(shù)-XX:+UseTLAB倚聚,則開(kāi)啟了本地線程分配緩沖(Thread local Allocation Buffer, TLAB)线衫,會(huì)為每個(gè)線程單獨(dú)在堆中劃分出一個(gè)TLAB,哪個(gè)線程需要分配內(nèi)存惑折,就先在該線程對(duì)應(yīng)的TLAB中分配內(nèi)存授账,當(dāng)TLAB用完,才在堆區(qū)的Eden中繼續(xù)申請(qǐng)一塊TLAB惨驶。

方法區(qū)

方法區(qū)是用于存放虛擬機(jī)加載的類(lèi)信息白热、常量、靜態(tài)變量粗卜、編譯后的代碼等數(shù)據(jù)屋确。

方法區(qū)特點(diǎn):

  • 線程共享
  • 方法區(qū)大小可固定也可以動(dòng)態(tài)擴(kuò)展。
  • 與堆區(qū)一樣不需要連續(xù)的物理內(nèi)存续扔,但要求邏輯連續(xù)攻臀。
  • 該區(qū)域的垃圾收集目標(biāo)主要是針對(duì)運(yùn)行時(shí)常量池的回收和對(duì)類(lèi)進(jìn)行卸載
  • 可能出現(xiàn)OutOfMemoryError異常测砂。

擴(kuò)展1:運(yùn)行時(shí)常量池:

class文件中有個(gè)常量池茵烈,運(yùn)行時(shí)常量池就是class文件中常量池經(jīng)過(guò)類(lèi)加載后存放的內(nèi)存區(qū)域。

常量池主要存放兩類(lèi)常量:字面量和符號(hào)引用砌些。

字面量指字符串呜投,聲明為final的常量值等;而符號(hào)引用是java編譯后生成的各種常量存璃,其包括:

  • 類(lèi)和接口的全限定名
  • 成員變量的名稱(chēng)和描述符
  • 方法的名稱(chēng)和描述符

jdk1.8之前仑荐,方法區(qū)是用永久代實(shí)現(xiàn)的,
jdk1.7以下的版本纵东,運(yùn)行時(shí)常量池是方法區(qū)的一部分粘招,而jdk1.7及之后的版本,運(yùn)行時(shí)常量池中的字符串常量池已經(jīng)不在方法區(qū)偎球,而是在java堆中開(kāi)辟了一塊區(qū)域作為字符串常量池洒扎。

在jdk1.8開(kāi)始辑甜,已經(jīng)沒(méi)有永久代的概念,譬如符號(hào)引用(Symbols)轉(zhuǎn)移到了native 堆中的元空間袍冷;字面量也在 java heap磷醋;類(lèi)的靜態(tài)變量(class statics)轉(zhuǎn)移到了java heap

擴(kuò)展2:常量是否只能在編譯期產(chǎn)生?
否胡诗,運(yùn)行期也可能將新的常量放入運(yùn)行時(shí)常量池中邓线,比如Stringintern方法。在jdk1.7的表現(xiàn)如下:

// 如果運(yùn)行時(shí)常量池中煌恢,存在"10"這個(gè)字符串常量
// 則將常量池中的字符串對(duì)象返回骇陈,
// 如果不存在,則直接在運(yùn)行時(shí)常量池中創(chuàng)建“10"這個(gè)字符串瑰抵,并將其返回你雌。
String s = String.valueOf(10).intern();

直接內(nèi)存

前面講的幾塊都屬于虛擬機(jī)管理的運(yùn)行時(shí)數(shù)據(jù)區(qū)域,java程序中也有可能會(huì)用到不是虛擬機(jī)運(yùn)行時(shí)內(nèi)存區(qū)域的一部分谍憔。這塊內(nèi)存我們通常稱(chēng)為直接內(nèi)存

  • 直接內(nèi)存不受java堆大小的限制匪蝙,但是受本機(jī)物理內(nèi)存的限制主籍。

  • 直接內(nèi)存也可能導(dǎo)致出現(xiàn)OutOfMemoryError異常习贫。

直接內(nèi)存的例子:
jdk 1.4 加入的NIO類(lèi),引入了一種基于通道Channel和緩沖區(qū)Buffer的IO方式千元。直接通過(guò)Native方法在java堆外的直接內(nèi)存中分配內(nèi)存, 通過(guò)存儲(chǔ)在java堆中的DirectByteBuffer對(duì)象作為這塊直接內(nèi)存的引用苫昌。操作DirectByteBuffer即可操作直接內(nèi)存,這樣做的好處是避免了要使用直接內(nèi)存的時(shí)候需要先復(fù)制到j(luò)ava堆中幸海。直接操作直接內(nèi)存更加高效祟身。

點(diǎn)贊是對(duì)我最大的鼓勵(lì)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市物独,隨后出現(xiàn)的幾起案子袜硫,更是在濱河造成了極大的恐慌,老刑警劉巖挡篓,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婉陷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡官研,警方通過(guò)查閱死者的電腦和手機(jī)秽澳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)戏羽,“玉大人担神,你說(shuō)我怎么就攤上這事∈蓟ǎ” “怎么了妄讯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵孩锡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我亥贸,道長(zhǎng)浮创,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任砌函,我火速辦了婚禮斩披,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘讹俊。我一直安慰自己垦沉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布仍劈。 她就那樣靜靜地躺著厕倍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贩疙。 梳的紋絲不亂的頭發(fā)上讹弯,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音这溅,去河邊找鬼组民。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悲靴,可吹牛的內(nèi)容都是我干的臭胜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼癞尚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼耸三!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起浇揩,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仪壮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后胳徽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體积锅,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年膜廊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乏沸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡爪瓜,死狀恐怖蹬跃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤蝶缀,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布丹喻,位于F島的核電站,受9級(jí)特大地震影響翁都,放射性物質(zhì)發(fā)生泄漏碍论。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一柄慰、第九天 我趴在偏房一處隱蔽的房頂上張望鳍悠。 院中可真熱鬧,春花似錦坐搔、人聲如沸藏研。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蠢挡。三九已至,卻和暖如春凳忙,著一層夾襖步出監(jiān)牢的瞬間业踏,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工涧卵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勤家,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓艺演,卻偏偏與公主長(zhǎng)得像却紧,于是被迫代替她去往敵國(guó)和親桐臊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胎撤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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

  • JVM內(nèi)存模型Java虛擬機(jī)(Java Virtual Machine=JVM)的內(nèi)存空間分為五個(gè)部分,分別是: ...
    光劍書(shū)架上的書(shū)閱讀 2,493評(píng)論 2 26
  • 一断凶、運(yùn)行時(shí)數(shù)據(jù)區(qū)域 Java虛擬機(jī)管理的內(nèi)存包括幾個(gè)運(yùn)行時(shí)數(shù)據(jù)內(nèi)存:方法區(qū)伤提、虛擬機(jī)棧、本地方法棧认烁、堆肿男、程序計(jì)數(shù)器,...
    加油小杜閱讀 1,514評(píng)論 1 15
  • 環(huán)境描述: 閱讀本文前却嗡,假定您已經(jīng)能夠啟動(dòng)單節(jié)點(diǎn)EOSIO node舶沛,如果還不能正確操作,請(qǐng)參考官方WIKI:ht...
    eosspark閱讀 2,417評(píng)論 0 2
  • “破睡須封不夜侯”窗价,不夜侯是茶的雅號(hào)如庭,它有著提神醒腦之功。毋庸置疑撼港,這是茶人三部曲的第二部坪它。 絮叨完第一部《南方有...
    小褲兜兒閱讀 2,678評(píng)論 1 3
  • 你試過(guò)深刻的感覺(jué)嗎骤竹? 心,總是莫名的會(huì)被感動(dòng)往毡。所以蒙揣,路過(guò)別人的故事,總是被劇情所左右开瞭,所以懒震,才有了后來(lái)發(fā)生的種種。...
    粟小萸閱讀 156評(píng)論 0 0