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