讀《深入理解Java虛擬機(jī)》讀書(shū)筆記

image.png
程序計(jì)數(shù)器:
- 為了保證程序能夠連續(xù)地執(zhí)行下去,處理器必須具有某些手段來(lái)確定下一條指令的地址,而程序計(jì)數(shù)器正是起到這種作用。
- 用來(lái)記錄當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)。字節(jié)碼解釋器的工作就是通過(guò)改變程序計(jì)數(shù)器來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令(分支,循環(huán),跳轉(zhuǎn),異常,線程恢復(fù)等)。
- JVM的多線程是通過(guò)線程輪流切換并分配處理器執(zhí)行時(shí)間來(lái)實(shí)現(xiàn)的。在一個(gè)確定時(shí)刻,一個(gè)處理器都只會(huì)執(zhí)行一條線程中的指令。因此為了線程切換后能恢復(fù)到正確執(zhí)行位置,每條線程需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線程之間計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ)。因此程序計(jì)數(shù)器的內(nèi)存區(qū)域是線程私有的內(nèi)存。
Java虛擬機(jī)棧:
- 線程私有的,生命周期和線程相同。他描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)會(huì)創(chuàng)建一個(gè)棧楨用于存儲(chǔ)局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接,方法出口等信息。
- 每個(gè)方法從調(diào)用到執(zhí)行完成,就對(duì)應(yīng)一個(gè)棧楨在虛擬機(jī)棧中從入棧到出棧。
- 平時(shí)所泛指的內(nèi)存中的“堆和?!崩锏臈V傅木褪沁@部分,或者說(shuō)是虛擬機(jī)棧中的局部變量表的部分。
- 局部變量表存放了編譯期可知的:基本數(shù)據(jù)類(lèi)型,引用數(shù)據(jù)類(lèi)型(其實(shí)就是對(duì)象的內(nèi)存地址的指針)和returnAddress類(lèi)型(指向了一條字節(jié)碼指令的地址)。
- 局部變量表所需的內(nèi)存空間在編譯器完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí),這個(gè)方法需要在楨中分配多大的局部變量控件是確定的。在方法運(yùn)行期間不會(huì)改變局部變量表的大小。
- 如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError;如果虛擬機(jī)可以動(dòng)態(tài)擴(kuò)展,但是擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存,拋出OutOfMemoryError。
本地方法棧:
- 作用和本地方法棧類(lèi)似,區(qū)別在于虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法服務(wù),而本地方法棧為虛擬機(jī)使用到的Native方法服務(wù)。
Java堆:
- 被所有線程共享的一塊內(nèi)存區(qū)域。
- 詞內(nèi)存區(qū)的唯一目的:存放所有的對(duì)象實(shí)例和數(shù)組。
- Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可。如果在堆中沒(méi)有內(nèi)存完成實(shí)例分配,并且堆也無(wú)法再擴(kuò)展時(shí),將會(huì)拋出OutOfMemoryError。
方法區(qū):
- 各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲(chǔ):已被虛擬機(jī)加載的類(lèi)信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后的代碼等。
- 垃圾收集行為在該區(qū)域比較少出現(xiàn)
- 這個(gè)區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類(lèi)型的卸載。
- 當(dāng)方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí),將拋出OutOfMemoryError。
運(yùn)行時(shí)常量池:
- Class文件中除了類(lèi)的版本,字段,方法,接口等描述信息外,還有一項(xiàng)信息是常量池。
- 在編譯器確定,在類(lèi)加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池
- 并不一定只有編譯器才能產(chǎn)生,運(yùn)行期也可以將新的常量放入池中,比如String類(lèi)對(duì)象的Intern()方法。
- 當(dāng)常量池?zé)o法申請(qǐng)到內(nèi)存時(shí)會(huì)拋出OutOfMemoryError(jdk7之后,常量池從方法區(qū)被移動(dòng)到堆區(qū),調(diào)用String#intern方法時(shí),如果存在堆中的對(duì)象,會(huì)直接保存對(duì)象的引用,而不會(huì)重新創(chuàng)建對(duì)象。)
直接內(nèi)存:
- 不屬于虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域。但是這部分內(nèi)存也會(huì)被頻繁使用,并且可能報(bào)OOM。
- JDK1.4后引入了NIO類(lèi):使用Native函數(shù)直接分配堆外內(nèi)存,然后通過(guò)一個(gè)存儲(chǔ)在java堆中的DirectByteBuffer對(duì)象作為這塊堆內(nèi)存的引用進(jìn)行操作。這樣能在一些場(chǎng)景中提高性能,因?yàn)楸苊饬嗽贘AVA堆和Native堆中來(lái)回復(fù)制數(shù)據(jù)。
- 直接內(nèi)存不會(huì)受到Java堆大小的限制,但是會(huì)受到本機(jī)總內(nèi)存(RAM等)大小和處理器尋址空間的限制。有時(shí)在配置-Xmx時(shí)因?yàn)楹雎粤酥苯觾?nèi)存,導(dǎo)致服務(wù)器各個(gè)內(nèi)存區(qū)域總和大于物理內(nèi)存限制,從而導(dǎo)致動(dòng)態(tài)拓展時(shí)出現(xiàn)OOM。