Java內(nèi)存區(qū)域與內(nèi)存溢出異常
運(yùn)行時(shí)數(shù)據(jù)區(qū)域
- 所有線程共享區(qū)域
- 方法區(qū)
- 常量池
- 堆
- 方法區(qū)
- 線程隔離數(shù)據(jù)取
- 虛擬機(jī)棧
- 本地方法棧
- 程序計(jì)數(shù)器
程序計(jì)數(shù)器
- 當(dāng)前線程所執(zhí)行字節(jié)碼行號(hào)指示器
- 每個(gè)線程有獨(dú)立的計(jì)數(shù)器
- 執(zhí)線程行JAVA方法計(jì)數(shù)器記錄字節(jié)碼指令地址;native方法計(jì)數(shù)器值為空
JAVA虛擬機(jī)棧
- 生命周期與虛擬機(jī)相同
- 是JAVA方法執(zhí)行的內(nèi)存模型
- 存儲(chǔ)了編譯器可知的基本類型和對(duì)象引用
- long屉栓、double會(huì)占用2個(gè)局部變量空間窗声,其余類型占一個(gè)
- 局部變量表所需空間在編譯器分配完成
虛擬機(jī)棧異常
- 線程請(qǐng)求棧深度>虛擬機(jī)允許深度 StackOverflowError
- 若虛擬機(jī)可以動(dòng)態(tài)擴(kuò)展评肆,但擴(kuò)展無法申請(qǐng)到足夠的內(nèi)存 OutOfMemoryError
本地方法棧
- 為虛擬機(jī)使用Native方法服務(wù)
JAVA堆
- 虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建
- 存放對(duì)象實(shí)例
- CG管理的主要區(qū)域
- 邏輯上連續(xù)
- 可擴(kuò)展(-Xmx 邻悬,-Xms)伟件,無法擴(kuò)展 OutOfMemoryError
方法區(qū)
- 存儲(chǔ)已被虛擬機(jī)加載的類信息笛钝、常量袜刷、靜態(tài)變量聪富、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)
運(yùn)行時(shí)常量池
- 是方法區(qū)的一部分,用于類加載后存放編譯期生成的字面量和符號(hào)引用
- 編譯期和運(yùn)行時(shí)都可以產(chǎn)生
直接內(nèi)存
- Native函數(shù)直接分配的堆外內(nèi)存著蟹,不是運(yùn)行時(shí)數(shù)據(jù)區(qū)內(nèi)存墩蔓,也不是虛擬機(jī)規(guī)范定義的內(nèi)存
虛擬機(jī)對(duì)象
對(duì)象的創(chuàng)建
- new指令:檢查指令參數(shù)是否能在常量池中定位到一個(gè)符號(hào)的引用,檢查符號(hào)代表的類是否已經(jīng)加載梢莽、解析和初始化過如果沒有先執(zhí)行初始化
- 加載檢查后為新生對(duì)象分配內(nèi)存
- 內(nèi)存規(guī)整:指針碰撞
- 內(nèi)存不整:空閑列表
- 堆是否規(guī)整由垃圾手機(jī)去是否帶壓縮整理功能決定
- 對(duì)象創(chuàng)建是否頻繁
- 并發(fā)
- 同步
- 內(nèi)存分配按線程劃分在不同空間
- 每個(gè)線程在堆中預(yù)分配一塊小內(nèi)存,稱為本地線程分配緩沖(Thread Local Allocation Buffer TLAB)钢拧。哪個(gè)線程需要分配內(nèi)存就在哪個(gè)線程的TLAB上分配,只有TLAB用完并分配新的TLAB時(shí)炕横,才同步鎖定源内。
- 并發(fā)
- 將分配到的內(nèi)存空間都初始化為零值。保證不賦值直接使用
- 對(duì)象設(shè)置(對(duì)象是哪個(gè)類的實(shí)例份殿,如何找到類的元數(shù)據(jù)信息膜钓、哈希碼、GC分代年齡信息卿嘲,他們存儲(chǔ)在對(duì)象頭中)
- 執(zhí)行<init>方法
對(duì)象的內(nèi)存布局
- 對(duì)象頭
- 存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)(哈希碼颂斜、GC分代年齡、鎖狀態(tài)標(biāo)志拾枣、線程持有的鎖沃疮、偏向線程ID、偏向時(shí)間戳等稱Mark Word梅肤、考慮虛擬機(jī)的空間效率司蔬,Mark Word設(shè)計(jì)成非固定的數(shù)據(jù)結(jié)構(gòu),根據(jù)對(duì)象復(fù)用自己的存儲(chǔ)空間)
- 類型指針
- 如果是數(shù)組還要存儲(chǔ)數(shù)組長(zhǎng)度
- 實(shí)例數(shù)據(jù)
- 存儲(chǔ)對(duì)象信息姨蝴,
- 相同寬度的字段被分配到一起俊啼,若滿足這個(gè)條件父類定義的變量會(huì)出現(xiàn)在子類之前
- 如果CompactFields參數(shù)為true,子類中較窄的變量可能會(huì)插入到父類變量空隙中
- 對(duì)齊填充
- 不是必然的左医,僅起占位符作用
- 自動(dòng)內(nèi)存管理系統(tǒng)要求對(duì)象大小必須8字節(jié)的整數(shù)倍授帕,當(dāng)對(duì)象實(shí)例沒有對(duì)齊就需要對(duì)齊補(bǔ)全
對(duì)象的訪問
- 通過棧上的refrence數(shù)據(jù)來操作堆上的對(duì)象
- 兩種訪問方式
- 通過句柄。
- Java堆將會(huì)劃分出一塊內(nèi)存作為句柄池浮梢,refrence中存儲(chǔ)的就是對(duì)象的句柄地址
- 句柄包含了對(duì)象實(shí)例數(shù)據(jù)與類型數(shù)據(jù)各自的具體地址
- 通過指針
- Java堆對(duì)象的布局必須考慮如何放置訪問類型和數(shù)據(jù)的相關(guān)信息
- refrence中存儲(chǔ)的是對(duì)象地址
- 通過句柄。
- 區(qū)別
- 句柄:refrence存儲(chǔ)的是穩(wěn)定句柄地址跛十,對(duì)象移動(dòng)只修改句柄實(shí)例數(shù)據(jù)指針,refrence不需修改
- 指針:速度快秕硝,節(jié)省指針定位時(shí)間開銷
OutOfMemoryError異常
Java堆溢出
- Java堆用于存儲(chǔ)實(shí)例對(duì)象偶器,只要不斷創(chuàng)建對(duì)象,并且保證GC Roots到對(duì)象之間有可達(dá)路徑來避免垃圾回收機(jī)制清除這些對(duì)象缝裤,那么在對(duì)象數(shù)量達(dá)到最大堆的容量限制后就會(huì)產(chǎn)生內(nèi)存溢出異常
- 內(nèi)存泄露(對(duì)象已經(jīng)死亡)
- 內(nèi)存溢出(調(diào)大物理內(nèi)存)
虛擬機(jī)棧和本地方法棧溢出
- 線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的最大深度屏轰,StackOverflowError
- 虛擬機(jī)在快粘時(shí)無法申請(qǐng)到足夠的內(nèi)存,OutOfMemoryError
- 單線程憋飞,無論是棧幀太大還是巡講棧容量太小霎苗,內(nèi)存無法分配時(shí),都是StackOverflowError
- 通過不斷創(chuàng)建線程可產(chǎn)生內(nèi)存溢出榛做,每個(gè)線程的棧分配的內(nèi)存越大唁盏,反而泳衣產(chǎn)生內(nèi)存溢出
方法區(qū)和運(yùn)行時(shí)常量池溢出
- 大量生成Class
本機(jī)內(nèi)存直接溢出
- NIO