Java內(nèi)存區(qū)域與內(nèi)存溢出異常
Java虛擬機在執(zhí)行Java程序的過程中會把它管理的內(nèi)存分為不同的若干個數(shù)據(jù)區(qū)域艺挪。
具體的情況如下圖所示:
我們可以可到運行時數(shù)據(jù)區(qū)主要有以下幾個部分組成:
- 程序計數(shù)器(Program Counter Register)
- Java虛擬機棧(VM Stack)
- 本地方法棧(Native Method Stack)
- Java堆(Heap)
- 方法區(qū)
我們一個個看分析一下這些部分的功能和特點。
程序計數(shù)器(Program Count Register)
程序計數(shù)器是一塊比較小的內(nèi)存區(qū)域兵扬,它的作用是:當(dāng)前線程所助興的字節(jié)碼的符號顯示器麻裳。
這里有一個需要注意的地方,即【當(dāng)前線程】周霉,也就是說程序計數(shù)器是每個線程獨享的掂器,它用于在線程切換后能恢復(fù)到上一次的正確執(zhí)行位置。
這個區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域俱箱。
Java虛擬機棧
Java虛擬機棧也是線程私有的国瓮,它的生命周期與線程相同。
這一部分描述的是Java方法執(zhí)行的內(nèi)存模型:
每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀(Stack Frame)狞谱,用于存儲局部變量表等信息乃摹。每一個方法被調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個棧幀從入棧到出棧的過程跟衅。
在虛擬機棧中最重要的部分是局部變量表孵睬, 存放了編譯期可知的基本數(shù)據(jù)類型,對象引用等等伶跷。
局部變量表所需的內(nèi)存空間在編譯期完成分配掰读。
這個區(qū)域可能拋出兩種異常:
- 如果線程請求的棧深度大于虛擬機所允許的深度: 拋出StackOverflowError
- 如果虛擬機椫肀罚可以動態(tài)擴展,當(dāng)擴展無法申請到足夠內(nèi)存時摩桶,拋出OutOfMemoryError
本地方法棧
本地方法棧與虛擬機椣睿基本一致,不同的是本地方法棧是位虛擬機使用到的Native方法服務(wù)的拢肆。在有些虛擬機中(Sun HotSpot)减响,直接把本地方法棧和虛擬機棧合二為一了。
Java堆
我們嘗試總結(jié)Java堆的特性如下:
- Java堆是被所有線程共享的一塊內(nèi)存區(qū)域
- 在虛擬機啟動時創(chuàng)建
- 這一區(qū)域的目的是:存放對象實例
- Java堆是垃圾收集器管理的主要區(qū)域郭怪,因此很多時候也被稱為“GC堆”
- Java堆可以處于物理上不連續(xù)的內(nèi)存空間支示,只要在邏輯上連續(xù)就行了
方法區(qū)
與Java堆一樣,方法區(qū)也是線程共享的內(nèi)存區(qū)域鄙才。
它用于存儲已被虛擬機加載的類信息颂鸿、常量、靜態(tài)變量咒循、即時編譯器編譯后的代碼等等据途。
垃圾收集行為在這個區(qū)域比較少見,回收目標(biāo)主要是常量池的回收和對類型的卸載叙甸。
運行時常量池(Runtime Constant Pool)
運行時常量池是方法區(qū)的一部分颖医,用于存放編譯器生成的各種字面量和符號引用。
運行時常量池具備動態(tài)性裆蒸,運行期間也可以將新的常量放入池中熔萧。