jvm簡介?
內(nèi)存劃分
java虛擬機(jī)按照運(yùn)行時內(nèi)存使用區(qū)域劃分如圖:
Paste_Image.png
一姨谷、程序計數(shù)器(Program Counter Register)
程序計數(shù)器就是記錄當(dāng)前線程執(zhí)行程序的位置泊柬,改變計數(shù)器的值來確定執(zhí)行的下一條指令,比如循環(huán)讳侨、分支、方法跳轉(zhuǎn)、異常處理,線程恢復(fù)都是依賴程序計數(shù)器來完成妓美。
Java虛擬機(jī)多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式實現(xiàn)的僵腺。為了線程切換能恢復(fù)到正確的位置鲤孵,每條線程都需要一個獨立的程序計數(shù)器,所以它是線程私有的辰如。
如果線程正在執(zhí)行的是一個Java方法普监,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是Native方法,這個計數(shù)器值則為空(Undefined)凯正。此內(nèi)存區(qū)域是唯一一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域毙玻。
二、java虛擬機(jī)棧(VM Stack)
java虛擬機(jī)棧是線程私有廊散,生命周期與線程相同桑滩。創(chuàng)建線程的時候就會創(chuàng)建一個java虛擬機(jī)棧。
虛擬機(jī)執(zhí)行java程序的時候允睹,每個方法都會創(chuàng)建一個棧幀运准,棧幀存放在java虛擬機(jī)棧中,通過壓棧出棧的方式進(jìn)行方法調(diào)用缭受。
棧幀又分為一下幾個區(qū)域:局部變量表胁澳、操作數(shù)棧、動態(tài)連接米者、方法出口等韭畸。
平時我們所說的變量存在棧中,這句話說的不太嚴(yán)謹(jǐn)蔓搞,應(yīng)該說局部變量存放在java虛擬機(jī)棧的局部變量表中胰丁。
java的8中基本類型的局部變量的值存放在虛擬機(jī)棧的局部變量表中,如果是引用型的變量喂分,則只存儲對象的引用地址隘马。
注意:
[if !supportLists]?[endif]當(dāng)用戶請求web服務(wù)器,每個請求開啟一個線程負(fù)責(zé)用戶的響應(yīng)計算(每個線程分配一個虛擬機(jī)椘薅ィ空間)酸员,如果并發(fā)量大時,可能會導(dǎo)致內(nèi)存溢出(OutOfMemoneyError)讳嘱,可以適當(dāng)?shù)陌衙總€虛擬機(jī)棧的大小適當(dāng)調(diào)小一點幔嗦,減少內(nèi)存的使用量來提高系統(tǒng)的并發(fā)量。
[if !supportLists]?[endif]當(dāng)椓ぬ叮空間調(diào)小以后邀泉,又會引發(fā)方法調(diào)用深度的的問題。因為钝鸽,每個方法都會生成一個棧幀汇恤,如果方法調(diào)用深度很深就意味著,棧里面存放大量的棧幀拔恰,可能導(dǎo)致棧內(nèi)存溢出(StackOverFlowError)因谎。
三、本地方法棧(Native Method Stack)
本地方法棧為虛擬機(jī)使用到本地方法服務(wù)(native)颜懊。本地方法棧為線程私有财岔,功能和虛擬機(jī)棧非常類似风皿。線程在調(diào)用本地方法時,來存儲本地方法的局部變量表匠璧,本地方法的操作數(shù)棧等等信息桐款。
本地方法:是非java語言實現(xiàn)的方法,例如夷恍,java調(diào)用C語言魔眨,來操作某些硬件信息。
四酿雪、堆(Heap):
堆是被所有線程共享的區(qū)域冰沙,實在虛擬機(jī)啟動時創(chuàng)建的。堆里面存放的都是對象的實例(new出來的對象都存在堆中)执虹。
我們平常所說的垃圾回收拓挥,主要回收的就是堆區(qū)。為了提升垃圾回收的性能袋励,又把堆分成兩塊區(qū)新生代(young)和年老代(old)侥啤,更細(xì)一點劃分新生代又可劃分為Eden區(qū)和2個Survivor區(qū)(From Survivor和To Survivor)。
如下圖結(jié)構(gòu):
Paste_Image.png
[if !supportLists]?[endif]Eden:新創(chuàng)建的對象存放在Eden區(qū)
[if !supportLists]?[endif]From Survivor和To Survivor:保存新生代gc后還存活的對象茬故。(使用復(fù)制算法盖灸,導(dǎo)致有一個Survivor空間浪費)Hotspot虛擬機(jī)新生代Eden和Survivor的大小比值為4:1,因為有兩個Survivor磺芭,所以Eden:From Survivor:To Survivor比值為8:1:1赁炎。
[if !supportLists]?[endif]老年代:對象存活時間比較長(經(jīng)過多次新生代的垃圾收集,默認(rèn)是15次)的對象則進(jìn)入老年的钾腺。
當(dāng)堆中分配的對象實例過多徙垫,且大部分對象都在使用,就會報內(nèi)存溢出異常(OutOfMemoneyError)放棒。
五姻报、方法區(qū)
方法區(qū)是被所有線程共享區(qū)域,用于存放已被虛擬機(jī)加載的類信息间螟,常量吴旋,靜態(tài)變量等數(shù)據(jù)。被Java虛擬機(jī)描述為堆的一個邏輯部分厢破。習(xí)慣是也叫它永久代(permanment generation)
永久代也會垃圾回收荣瑟,主要針對常量池回收,類型卸載(比如反射生成大量的臨時使用的Class等信息)摩泪。
常量池用于存放編譯期生成的各種字節(jié)碼和符號引用笆焰,常量池具有一定的動態(tài)性,里面可以存放編譯期生成的常量加勤;運(yùn)行期間的常量也可以添加進(jìn)入常量池中仙辟,比如string的intern()方法。
當(dāng)方法區(qū)滿時鳄梅,無法在分配空間叠国,就會拋出內(nèi)存溢出的異常(OutOfMemoneyError)。
java8中已經(jīng)沒有方法區(qū)了戴尸,取而代之的是元空間(Metaspace)粟焊。
六:直接內(nèi)存
直接內(nèi)存(Direct Memory)并不是虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域孙蒙,但是這部分內(nèi)存也被頻繁地使用项棠,而且也可能導(dǎo)致OutOfMemoryError異常出現(xiàn)。
JDK1.4加的NIO中挎峦,ByteBuffer有個方法是allocateDirect(int capacity) 香追,這是一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存坦胶,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作透典。這樣能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù)顿苇。
jvm內(nèi)存模型 就是這么簡單峭咒,如果看完還不太理解的,就多看幾遍纪岁,把 jvm 內(nèi)存模型背下來凑队,記到腦子里。