柚子快報(bào)邀請(qǐng)碼778899分享:java 算法 JVM總結(jié)1
柚子快報(bào)邀請(qǐng)碼778899分享:java 算法 JVM總結(jié)1
Java堆
Java堆是用于存儲(chǔ)對(duì)象示例、數(shù)組等,是線程共享的,當(dāng)堆中沒有內(nèi)存空間可分配給實(shí)例,也無法再擴(kuò)展時(shí),則拋出OutOfMemoryError異常,其存儲(chǔ)架構(gòu)圖如下:
包含了新生代、老生代,新生代包含了Eden、from、to三部分,其中from、to屬于Survivors,根據(jù)JVM的策略,在經(jīng)過幾次垃圾收集后,任然存活于Survivor的對(duì)象將被移動(dòng)到老年代區(qū)間,老年代主要保存生命周期長(zhǎng)的對(duì)象,一般是一些老的對(duì)象。新生代與老年代的大小比1:2,Eden、from、to的內(nèi)存大小比為8:1:1。
而對(duì)于JDK1.8,在本地內(nèi)存中,有一塊元空間,用于保存的運(yùn)行時(shí)常量池、類常量池,其目的是為了防止存在出現(xiàn)OOM(OutOfMemoryError)。而上述內(nèi)容在JDK1.7保存在永久代,即JDK1.8相當(dāng)于把JDK1.7的永久代移動(dòng)到本地內(nèi)存中,JDK1.6使用永久代實(shí)現(xiàn)方法區(qū)。
虛擬機(jī)棧
虛擬機(jī)棧是每個(gè)線程運(yùn)行時(shí)所需的存儲(chǔ)空間,其特征是先進(jìn)后出。對(duì)于每個(gè)虛擬機(jī)棧,由多個(gè)棧幀組成,每個(gè)棧幀對(duì)應(yīng)每一次調(diào)用時(shí)所占用的內(nèi)存,每個(gè)線程僅有一個(gè)活動(dòng)棧幀,活動(dòng)棧幀對(duì)應(yīng)著正在被調(diào)用的方法,棧幀主要存放局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。虛擬機(jī)棧結(jié)構(gòu)如下圖:
由于每次調(diào)用完成后,虛擬機(jī)棧會(huì)彈出棧幀,因此垃圾回收時(shí)不考慮棧。在線程安全性方面,如果方法內(nèi)局部變量沒有逃離方法的作用范圍,它是線程安全的;如果是局部變量引用了對(duì)象,并逃離方法的作用范圍,需要考慮線程安全,下圖為幾種常見的情況:
對(duì)于m1函數(shù),由于sb為局部變量且未被返回,因此不存在線程安全問題;m2中,可能會(huì)存在多線程調(diào)用的情況,而sb傳入并對(duì)其操作后,其內(nèi)容會(huì)發(fā)生改變,因此存在線程安全問題;m3中,由于sb被返回了,返回后可能會(huì)有線程對(duì)其進(jìn)行操作,因此存在線程安全問題。
當(dāng)棧幀過多(如遞歸調(diào)用)或過大時(shí),會(huì)導(dǎo)致棧溢出。
堆和棧的區(qū)別
1、棧內(nèi)存一般會(huì)用來存儲(chǔ)局部變量和方法調(diào)用,但堆內(nèi)存是用來存儲(chǔ)Java對(duì)象和數(shù)組的;堆會(huì)GC垃圾回收,而棧不會(huì)。 2、棧內(nèi)存是線程私有的,而堆內(nèi)存是線程共有的。 3、兩者異常錯(cuò)誤不同,但如果棧內(nèi)存或者堆內(nèi)存不足都會(huì)拋出異常。棧空間不足:java.lang.StackOverFlowError;堆空間不足:java.lang.OutOfMemoryError。
程序計(jì)數(shù)器
程序計(jì)數(shù)器是線程私有的,內(nèi)部保存字節(jié)碼的行號(hào),用于記錄正在執(zhí)行的字節(jié)碼的地址。
在線程切換并完成執(zhí)行后,返回切換前的線程,則程序計(jì)數(shù)器存放了切換器字節(jié)碼執(zhí)行的地址,因此可以根據(jù)這個(gè)地址繼續(xù)執(zhí)行。程序計(jì)數(shù)器是JVM規(guī)范中唯一一個(gè)沒有規(guī)定出現(xiàn)OOM的區(qū)域,所以這個(gè)空間也不會(huì)進(jìn)行GC。
方法區(qū)
概述
方法區(qū)是線程共享的區(qū)域,其并不真實(shí)存在,屬于 Java 虛擬機(jī)規(guī)范中的一個(gè)邏輯概念。方法區(qū)用于存放已被 JVM 加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼緩存等;其在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,關(guān)閉時(shí)釋放。如果方法區(qū)域中的內(nèi)存無法滿足分配請(qǐng)求,則會(huì)拋OutOfMemoryError: Metaspace在JDK1.8后,使用元空間實(shí)現(xiàn)方法區(qū)。
常量池
常量池可以看作是一張表,虛擬機(jī)指令根據(jù)這張常量表找到要執(zhí)行的類名、方法名、參數(shù)類型、字面量等信息,通過指令:javav -p xxx.class可得到常量池,常量池的參數(shù)標(biāo)題為“Constant pool”,效果如下圖所示:
運(yùn)行時(shí)常量池
對(duì)于*.class 文件中的,當(dāng)該類被加載,它的常量池信息就會(huì)放入運(yùn)行時(shí)常量池,并把里面的符號(hào)地址變?yōu)檎鎸?shí)地址。
直接內(nèi)存
不受JVM內(nèi)存回收管理,是虛擬機(jī)的系統(tǒng)內(nèi)存,常見于 NIO 操作時(shí),用于數(shù)據(jù)緩沖區(qū),分配回收成本較高,但讀寫性能高,不受 JVM 內(nèi)存回收管理,傳統(tǒng)阻塞IO與NIO對(duì)比如下:
下圖為傳統(tǒng)阻塞IO,當(dāng)需要讀取數(shù)據(jù)時(shí),進(jìn)入內(nèi)核態(tài),磁盤文件的數(shù)據(jù)依次經(jīng)過系統(tǒng)緩存區(qū)、java緩沖區(qū)byte[](位于Java堆內(nèi)存),最后才完成讀取并返回用戶態(tài)。
下圖是NIO傳輸數(shù)據(jù)的流程,在這個(gè)里面主要使用到了一個(gè)直接內(nèi)存,不需要在堆中開辟空間進(jìn)行數(shù)據(jù)的拷貝,jvm可以直接操作直接內(nèi)存,從而使數(shù)據(jù)讀寫傳輸更快。
柚子快報(bào)邀請(qǐng)碼778899分享:java 算法 JVM總結(jié)1
好文閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。