了解java虛擬機(jī)內(nèi)存劃分和各模塊作用使得我們更加清楚如何使用內(nèi)存咳促,防止內(nèi)存溢出和泄露。
一赏半、Java內(nèi)存區(qū)域的劃分
1.程序計(jì)數(shù)器(Program Counter Register)
這是一塊線程私有區(qū)域义起,可以看做是當(dāng)前線程執(zhí)行的的字節(jié)碼的行號(hào)指示器。它里面存儲(chǔ)的是線程將要執(zhí)行的下一條指令的地址脖咐。
唯一一個(gè)JVM中沒(méi)有規(guī)定任何OOM情況的區(qū)域
2.java虛擬機(jī)棧
同樣也是線程私有區(qū)域它的單位(即棧內(nèi)元素)是棧幀铺敌,棧的生命周期隨線程調(diào)用方法而起(入棧),隨線程方法調(diào)用結(jié)束而結(jié)束(出棧)屁擅。整個(gè)虛擬機(jī)棧的生命周期跟線程保持一致偿凭。
棧幀到底是什么? 它存儲(chǔ)著以下:
- 局部變量表:編譯期可知的各種基本數(shù)據(jù)類型派歌,對(duì)象引用類型弯囊。編譯期確定大小,不會(huì)改變胶果。
- 操作數(shù)棧:指令的執(zhí)行本質(zhì)是一系列運(yùn)算常挚,入棧出棧的過(guò)程。
- 動(dòng)態(tài)鏈接
- 方法返回地址:方法執(zhí)行完畢需要回到調(diào)用方法的入口處稽物。
虛擬機(jī)棧會(huì)發(fā)生兩種異常:
- OOM:動(dòng)態(tài)擴(kuò)展虛擬機(jī)內(nèi)存空間時(shí)不夠時(shí)會(huì)導(dǎo)致OOM
- 線程調(diào)用方法層級(jí)過(guò)深奄毡,超出虛擬機(jī)所允許的最大長(zhǎng)度導(dǎo)致StackOverflow
3.本地方法棧
- 與虛擬機(jī)棧相似,但是是為Native方法服務(wù)的贝或。
- 會(huì)拋出Stack Overflow和OOM兩種異常
4.Java堆
- 作用:存放對(duì)象實(shí)例
- 是否線程私有:否吼过,線程共享锐秦。因此存在并發(fā)安全問(wèn)題
- 是GC回收的主要作用區(qū)域。
- 從內(nèi)存回收看可以分為新生代和老年代盗忱,從內(nèi)存分配看堆中可以劃分出多個(gè)線程私有分分配緩沖區(qū)
- 可以不要求物理上連續(xù)的空間
- 存在OOM
5.方法區(qū)
- 線程共享
- 存儲(chǔ)已被虛擬機(jī)加載的類信息酱床、常亮、靜態(tài)變量趟佃、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)扇谣。
- GC時(shí)主要是針對(duì)常量池的回收和對(duì)類型的卸載
類卸載條件:
- 堆中不存在該類的任何實(shí)例
- 加載該類的classLoader已經(jīng)被回
- 該類對(duì)應(yīng)的java.lang.class類對(duì)象沒(méi)有在任何地方被引用
- 無(wú)法在任何地方通過(guò)反射訪問(wèn)該類的任何方法
6.運(yùn)行時(shí)常量池
- 本質(zhì)是屬于方法區(qū)的運(yùn)行時(shí)常量池。
- 存放編譯期生成的各種字面量和符號(hào)引用
- 并非編譯期預(yù)置于class的常量才能進(jìn)入常量池闲昭,通過(guò)String的intern()方法也能將常量動(dòng)態(tài)加入常量池
- 會(huì)存在OOM
二罐寨、對(duì)象的訪問(wèn)定位
Java程序需要通過(guò)棧上的reference數(shù)據(jù)訪問(wèn)堆上的對(duì)象,如何通過(guò)這個(gè)reference得到堆上的對(duì)象呢序矩?
1. 使用句柄的方式
在java堆中開(kāi)辟一個(gè)區(qū)域?qū)iT(mén)存儲(chǔ)對(duì)象實(shí)例地址和類型數(shù)據(jù)信息地址的內(nèi)存塊叫做句柄池
優(yōu)點(diǎn):
reference中存儲(chǔ)的是穩(wěn)定的句柄地址鸯绿,對(duì)象被移動(dòng)的時(shí)候只會(huì)改變句柄中的實(shí)例數(shù)據(jù)指針,reference本身不需修改
句柄池
2. 使用直接指針的方式
reference中存儲(chǔ)的直接是對(duì)象地址簸淀,再通過(guò)對(duì)象找到類型信息的地址
優(yōu)點(diǎn):減少一次hash定位的過(guò)程瓶蝴,速度更快
直接指針