Java內(nèi)存區(qū)域與內(nèi)存溢出異常

正文

一. 基本概念

在開始講解之前, 需要先明確關(guān)于?JVM?的一些基本概念

我們都知道,?Java?是一個(gè)跨平臺(tái)的語言,?Java?跨平臺(tái)的基本支撐其實(shí)就是?JVM?對(duì)操作系統(tǒng)底層細(xì)節(jié)的屏蔽, 相當(dāng)于加了一個(gè)中間層(計(jì)算機(jī)中的任何問題都可以加一個(gè)中間層解決~),?Java?不再像?C/C++?等語言一樣直接翻譯為針對(duì)特殊平臺(tái)的機(jī)器碼, 而是翻譯為字節(jié)碼, 也即是我們的?class?文件, 下圖大概可以比較簡(jiǎn)明的概括了~; 字節(jié)碼就相當(dāng)于?Java?世界中的匯編, 而?JVM?則不是跨平臺(tái)的, 只是不同平臺(tái)的?JVM?都能識(shí)別和運(yùn)行標(biāo)準(zhǔn)格式的字節(jié)碼文件而已

關(guān)于?JVM?運(yùn)行?class?文件, 我覺得下圖已經(jīng)可以比較準(zhǔn)確的表達(dá)了

我們下面要講的就是?Runtime Data Area?部分

二. 運(yùn)行時(shí)數(shù)據(jù)區(qū)

JVM?會(huì)在執(zhí)行?Java?程序的時(shí)候把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū), 如下:

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

線程私有

2.1.1 存儲(chǔ)數(shù)據(jù)類型

指向下一條需要執(zhí)行的字節(jié)碼指令; 如果線程正在執(zhí)行一個(gè)?Java?方法, 該計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址; 如果正在執(zhí)行?Native?方法, 該計(jì)數(shù)器值則為空(?Undefined?)

2.1.2 異常情況

該區(qū)域是是唯一一個(gè)在?Java?虛擬機(jī)中沒有規(guī)定任何?OutOfMemoryError?情況的區(qū)域

2.2 Java虛擬機(jī)棧

線程私有

2.2.1 存儲(chǔ)數(shù)據(jù)類型

描述?Java?方法執(zhí)行的內(nèi)存模型, 每個(gè)方法調(diào)用就對(duì)應(yīng)著一個(gè)棧幀的入棧和出棧; 一個(gè)棧幀里面存儲(chǔ)了局部變量表, 操作數(shù)棧, 動(dòng)態(tài)鏈接, 方法出口等信息

局部變量表存儲(chǔ)了編譯器可知的各種基本數(shù)據(jù)類型, 對(duì)象引用,?returnAddress?; 局部變量表的大小在編譯期間即可確定, 運(yùn)行期間大小不變

2.2.2 異常情況

StackOverflowError?: 線程請(qǐng)求棧深度大于虛擬機(jī)允許深度

異常示例代碼:

public?class?JavaVMStackSOF?{????private?int?stackLength?=?1;????public?void?stackLeak()?{

????????stackLength++;

????????stackLeak();

????}????public?static?void?main(String[]?args)?{

????????JavaVMStackSOF?sof?=?new?JavaVMStackSOF();????????try?{

????????????sof.stackLeak();

????????}?catch?(Throwable?e)?{

????????????System.out.println("Stack?Length:?"?+?sof.stackLength);????????????throw?e;

????????}

????}

}

OutOfMemoryError?: 虛擬機(jī)棧動(dòng)態(tài)擴(kuò)展時(shí)無法申請(qǐng)到足夠內(nèi)存

異常示例代碼:

public?class?JavaVMStackOOM?{????private?void?dontStop()?{????????while?(true)?{

????????}

????}????public?void?stackLeakByThread()?{????????while?(true)?{????????????new?Thread(new?Runnable()?{????????????????@Override

????????????????public?void?run()?{

????????????????????dontStop();

????????????????}

????????????}).start();

????????}

????}????public?static?void?main(String[]?args)?{

????????JavaVMStackOOM?oom?=?new?JavaVMStackOOM();

????????oom.stackLeakByThread();

????}

}

注: 由于操作系統(tǒng)分配給每個(gè)進(jìn)程的內(nèi)存空間是有限制的, 所以如果是由于建立過多的線程導(dǎo)致內(nèi)存溢出, 在不能減少線程數(shù)或者更換?64?位虛擬機(jī)的情況下, 可以選擇通過減少最大堆和減少棧容量來換取更多的線程

2.3 本地方法棧

線程私有

2.3.1 存儲(chǔ)數(shù)據(jù)類型

和虛擬機(jī)棧類似, 只是本地方法棧提供的是?Native?方法服務(wù)

2.3.2 異常情況

StackOverflowError?和?OutOfMemoryError

2.4 Java堆

線程共享

垃圾收集管理的主要區(qū)域

2.4.1 存儲(chǔ)數(shù)據(jù)類型

幾乎所有的對(duì)象實(shí)例都在這里分配

2.4.2 異常情況

OutOfMemoryError

異常示例:

public?class?JavaVMHeapOOM?{????static?class?HeapOOM?{

????}????public?static?void?main(String[]?args)?{????????List<HeapOOM>?list?=?new?ArrayList();????????while?(true)?{????????????list.add(new?HeapOOM());

????????}

????}

}

2.5 方法區(qū)

線程共享

該區(qū)域的垃圾回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類型的卸載

2.5.1 存儲(chǔ)數(shù)據(jù)類型

存儲(chǔ)已被虛擬機(jī)加載的類信息, 常量, 靜態(tài)變量, 即使編譯器編譯后的代碼等數(shù)據(jù)

2.5.2 運(yùn)行時(shí)常量池

運(yùn)行時(shí)常量池是方法區(qū)的一部分, 但是?JDK6?之后, 常量池被放入了堆中;

Class?文件中也有常量池部分, 即編譯期生成的各種字面量和符號(hào)引用, 這部分將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中, 此外還會(huì)把翻譯出來的直接引用也存儲(chǔ)在運(yùn)行時(shí)常量池中

運(yùn)行時(shí)常量池相對(duì)于?Class?文件常量池的另外一個(gè)最重要的特征是具備動(dòng)態(tài)性, 即運(yùn)行期間也可以將新的常量放入池中, 比如?String?的?intern()?方法

String.intern()?作用是: 如果字符串常量池中已經(jīng)包含一個(gè)等于此?String?對(duì)象的字符串, 則返回代表池中這個(gè)字符串的?String?對(duì)象; 否則, 將此?String?對(duì)象包含的字符串添加到常量池中, 并且返回此?String?對(duì)象的引用

同樣, 收方法區(qū)的限制, 當(dāng)常量池?zé)o法再申請(qǐng)到內(nèi)存時(shí)會(huì)拋出?OutOfMemoryError

2.5.3 異常情況

OutOfMemoryError?: 方法區(qū)無法滿足內(nèi)存分配需求

異常示例:

public?class?RuntimeConstantPoolOOM?{

????public?static?void?main(String[]?args)?{????????List<String>?list?=?new?ArrayList<>();????????int?i?=?0;????????while?(true)?{

????????????list.add(String.valueOf(i++).intern());

????????}

????}

}

2.6 直接內(nèi)存

直接內(nèi)存不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分, 但是也被頻繁使用, 如: 在?JDK1.4?中新加入了?NIO?類, 引入了一種基于通道(?Chanel?)和緩沖區(qū)(?Buffer?)的?I/O?方式, 它可以使用?Native?函數(shù)庫(kù)直接分配堆外內(nèi)存, 然后通過一個(gè)存儲(chǔ)在Java?堆中的?DirectByteBuffer?對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作, 避免了在?Java?堆和?Native?堆中來回復(fù)制數(shù)據(jù), 提高性能

同樣會(huì)產(chǎn)生?OutOfMemoryError

三. 常見問題

注: 下文摘自文末參考鏈接, 權(quán)作個(gè)人筆記~

程序運(yùn)行永遠(yuǎn)都是在棧中進(jìn)行的,因而參數(shù)傳遞時(shí), 只存在傳遞基本類型和對(duì)象引用的問題, 不會(huì)直接傳對(duì)象本身; 但是傳引用的錯(cuò)覺是如何造成的呢? 在運(yùn)行棧中, 基本類型和引用的處理是一樣的, 都是傳值, 所以, 如果是傳引用的方法調(diào)用, 也同時(shí)可以理解為“傳引用值”的傳值調(diào)用, 即引用的處理跟基本類型是完全一樣的; 但是當(dāng)進(jìn)入被調(diào)用方法時(shí), 被傳遞的這個(gè)引用的值, 被程序解釋(或者查找)到堆中的對(duì)象, 這個(gè)時(shí)候才對(duì)應(yīng)到真正的對(duì)象; 如果此時(shí)進(jìn)行修改; 修改的是引用對(duì)應(yīng)的對(duì)象; 而不是引用本身; 即: 修改的是堆中的數(shù)據(jù); 所以這個(gè)修改是可以保持的了

我產(chǎn)生的對(duì)象不多呀, 為什么還會(huì)產(chǎn)生?OutOfMemory??

答: 你繼承層次忒多了,?Heap?中產(chǎn)生的對(duì)象是先產(chǎn)生父類, 然后才產(chǎn)生子類, 明白不?

OutOfMemory?錯(cuò)誤分幾種? 答: 分兩種, 分別是?OutOfMemoryError:java heap size?和?OutOfMemoryError: PermGen space?, 兩種都是內(nèi)存溢出,?heap size?是說申請(qǐng)不到新的內(nèi)存了, 這個(gè)很常見,檢查應(yīng)用或調(diào)整堆內(nèi)存大小;PermGenspace?是因?yàn)橛谰么鎯?chǔ)區(qū)滿了, 這個(gè)也很常見, 一般在熱發(fā)布的環(huán)境中出現(xiàn), 是因?yàn)槊看伟l(fā)布應(yīng)用系統(tǒng)都不重啟, 久而久之永久存儲(chǔ)區(qū)中的死對(duì)象太多導(dǎo)致新對(duì)象無法申請(qǐng)內(nèi)存, 一般重新啟動(dòng)一下即可

為什么不建議在程序中顯式的生命?System.gc()?? 答: 因?yàn)轱@式聲明是做堆內(nèi)存全掃描, 也就是?Full GC?, 是需要停止所有的活動(dòng)的(?Stop The World Collection?), 你的應(yīng)用能承受這個(gè)嗎?

如果你想學(xué)好JAVA這門技術(shù),也想在IT行業(yè)拿高薪,可以參加我們的訓(xùn)練營(yíng)課程,選擇最適合自己的課程學(xué)習(xí),技術(shù)大牛親授,8個(gè)月后,進(jìn)入名企拿高薪。我們的課程內(nèi)容有:Java工程化、高性能及分布式、高性能、深入淺出。高架構(gòu)。性能調(diào)優(yōu)、Spring,MyBatis,Netty源碼分析和大數(shù)據(jù)等多個(gè)知識(shí)點(diǎn)。如果你想拿高薪的,想學(xué)習(xí)的,想就業(yè)前景好的,想跟別人競(jìng)爭(zhēng)能取得優(yōu)勢(shì)的,想進(jìn)阿里面試但擔(dān)心面試不過的,你都可以來,q群號(hào)為:180705916 進(jìn)群免費(fèi)領(lǐng)取學(xué)習(xí)資料。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容