java堆栈句柄,深入了解JVM—内存区域
1.Java虛擬機運行時數(shù)據(jù)區(qū)
在前面的幾篇博文中,我們只是簡單的把內(nèi)存區(qū)域分為了堆和棧,但其實,這種分法是十分粗糙的,jvm在實際運行的時候,內(nèi)存區(qū)域的劃分絕對不是那么簡簡單單的就兩塊,我們一起看下面這個圖就知道了。
從上圖我們知道了,JVM虛擬機運行時數(shù)據(jù)區(qū)主要劃分為:方法區(qū)、虛擬機棧、本地方法棧、堆、程序計數(shù)器。
1.1程序計數(shù)器
雖然在上圖中,程序計數(shù)器這塊占用的區(qū)域畫的很大,但其實,在內(nèi)存中,它只是較小的一塊內(nèi)存空間。
1)生命周期:線程私有的,與它所綁定的線程相同。
2)作用:當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器,簡單的可以理解成,程序計數(shù)器記錄著下一行要執(zhí)行的代碼行數(shù),比如我們經(jīng)常寫的分支、循環(huán)、跳轉(zhuǎn)都是通過改變該計數(shù)器的值來完成的。如果一個線程正在執(zhí)行的是Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在執(zhí)行的是Native方法,這個計數(shù)器則為空。
3)程序運行時該區(qū)域可能發(fā)生的異常:此內(nèi)存區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
1.2Java虛擬機棧
其實這里所說的棧,就是前面我們博文中粗糙分法堆棧中的棧了。精確的講的話,其實是Java虛擬機棧中的局部變量表部分吧;同時這里是專門為虛擬機執(zhí)行java方法服務(wù)的,為什么這么說呢?因為內(nèi)存區(qū)域中還有本地方法棧,本地方法棧是只為虛擬機使用到的Native方法服務(wù)的。
1)生命周期:線程私有的,與它所綁定的線程相同。
2)作用:每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀,用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。每個方法被調(diào)用直至執(zhí)行完成的過程就對應(yīng)著一個棧幀在虛擬機棧中從入棧到出棧的過程。其中局部變量表存儲的是以下類型:
①基本數(shù)據(jù)類型:boolean, byte, char, short, int, float, long, double;
②對象引用:reference類型,它不等于對象本身,根據(jù)不同的虛擬機實現(xiàn),它可能是一個指向?qū)ο笃鹗嫉刂返囊梦恼?#xff0c;也可能是指向一個對象句柄或其他與此對象有關(guān)的位置;
③returnAddress類型:指向了一條字節(jié)碼指令的地址,簡單的說,就是下一條要執(zhí)行的代碼的地址;
3)程序運行時該區(qū)域可能發(fā)生的異常:
①線程請求棧深度 > 虛擬機允許深度,拋出StackOverflowError;eg:
package edu.outofmemoryerror.heap;
public class JavaStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
/**
* -Xss128K
* @param args
*/
public static void main(String[] args) {
JavaStackSOF sf = new JavaStackSOF();
sf.stackLeak();
}
}
運行結(jié)果是:
注意在運行的時候注意要改變那些參數(shù);如果是通過控制臺命令來執(zhí)行的,可以直接跟在Java命令之后書寫,如果是通過Eclipse來執(zhí)行的可以在下面頁面添加上再來執(zhí)行,如圖:
②該棧允許動態(tài)拓展,當(dāng)無法申請到足夠的空間時,拋出OutOfMemoryError異常;
1.3本地方法棧
本地方法棧跟虛擬機棧相似,他們的區(qū)別在前面已經(jīng)提過了:本地方法棧是只為虛擬機使用到的Native方法服務(wù)的,而java虛擬機棧是專門為虛擬機執(zhí)行java方法服務(wù)的。
1)生命周期:線程私有的,與它所綁定的線程相同。
2)作用:為使用到的Native方法服務(wù)。
3)程序運行時該區(qū)域可能發(fā)生的異常:跟虛擬機棧一樣。
1.4Java堆(Java Heap)
對于很多應(yīng)用來說,Java堆是Java虛擬機所管理的內(nèi)存中最大的一塊。
1)生命周期:線程共享的,在虛擬機啟動時就創(chuàng)建
2)作用:存放對象實例,幾乎所有的對象實例以及數(shù)組都在這里分配內(nèi)存。
3)程序運行時該區(qū)域可能發(fā)生的異常:堆中沒有內(nèi)存完成實例分配或者堆無法再拓展時,將會拋出OutOfMemoryError異常,eg:
package edu.outofmemoryerror.heap;
import java.util.ArrayList;
import java.util.List;
public class HeapOOM {
static class OOMObject { }
/**
* -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
* @param args
*/
public static void main(String[] args) {
List list = new ArrayList();
while (true) {
list.add(new OOMObject());
}
}
}
運行結(jié)果:
1.5方法區(qū)(Method Area)
方法區(qū)有一個別名叫做非堆(Non-Heap),對于習(xí)慣在HotSpot虛擬機上開發(fā)和部署程序的開發(fā)者來說,它也叫“永久代”。
1)生命周期:線程共享的,在虛擬機啟動時就創(chuàng)建
2)作用:存儲已被虛擬機加載的類信息、常量、靜態(tài)變量,也就是編譯器編譯后的代碼數(shù)據(jù)。
3)程序運行時該區(qū)域可能發(fā)生的異常:當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,將會拋出OutOfMemoryError異常。比如java.lang.OutOfMemoryError,這個錯誤其實對于經(jīng)常使用SSh框架進行開發(fā)的我們應(yīng)該很熟悉,它就是由于方法區(qū)空間不足導(dǎo)致的內(nèi)存溢出。
因為該區(qū)域中的垃圾收集行為是比較少出現(xiàn)的,所以對象相當(dāng)于永久不被回收一般,所以在這個區(qū)域就叫做“永久代”了。
1.5.1運行時常量池
運行常量池是方法區(qū)的一部分。
1)生命周期:線程共享的,在虛擬機啟動時就創(chuàng)建
2)作用:存放Class文件中類的版本、字段、方法、接口等描述信息外,還有一些信息是常量池,用于存放編譯期間生產(chǎn)的各種字面量和符號引用,這部分內(nèi)容將在類加載后存放到方法區(qū)的運行是常量池中。
3)程序運行時該區(qū)域可能發(fā)生的異常:運行時是方法區(qū)的一部分,自然收到方法區(qū)內(nèi)存的限制,當(dāng)常量池?zé)o法在申請到內(nèi)存是就會拋出OutOfMemoryError異常
看到這里,不知道大家會不會想起前面一篇博文中我們說的JVM對字符串的處理呢?
沒錯,在那篇博文中講的Java字符串緩沖池,其實就是運行時常量池的一部分,而運行池是在方法區(qū)中的,方法區(qū)中的垃圾收集行為是幾乎沒有的,相當(dāng)于不會被回收。這就是前面的JVM對字符串處理的原理了。
1.6直接內(nèi)存
其實直接內(nèi)存不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中的一部分,但是這部分也頻繁的使用,也可能會導(dǎo)致OutOfMemoryError異常的出現(xiàn)。
2引用對象的訪問
在之前我們有一篇博文講了關(guān)于對象與內(nèi)存管理的知識,但是那里只是比較簡單的,下面我們更加深入了解一下,在Java語言中,對象訪問是如何進行的?
其實對象訪問并不像前面那篇博文中講的那么簡單只涉及到堆和棧,因為即使是最簡單的訪問,也會涉及到Java虛擬機棧,Java堆,方法區(qū)這三個重要的內(nèi)存區(qū)域。之所以只講堆和棧是因為開發(fā)人員在大多數(shù)情況下關(guān)注的最多的是堆和棧兩個區(qū)域。
關(guān)于基本類型的訪問,在前面那篇博文中我們已經(jīng)講了,下面,我們先提一下相關(guān)的概念:
1)對象類型數(shù)據(jù):指的是該對象的對象類型,父類,實現(xiàn)的接口,方法等,其實就是前面說的,方法區(qū)存儲的是加載的對象的信息。
2)對象實例數(shù)據(jù):指的是該類實例化后的對象的實例變量的值。
接著,我們繼續(xù)重點深入講解下關(guān)于引用類型對象的兩種訪問方式:
1)使用句柄方式訪問
使用這種對象的訪問方式的優(yōu)勢是:存儲的是穩(wěn)定的句柄地址,在對象移動時只會改變句柄中的實例數(shù)據(jù)指針,而reference本身不用改變。
使用句柄池訪問方式,Java堆中將會劃分出一塊內(nèi)存來作文句柄池,reference中存儲的是對象的句柄地址,而句柄中包含了對象實力數(shù)據(jù)和類型數(shù)據(jù)各自的具體地址信息。
其實從上圖中,我們還能看出一個知識點,那就是:64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量空間
2)使用直接指針訪問方式
使用這種訪問方式的優(yōu)勢是:訪問速度快。
總結(jié)
以上是生活随笔為你收集整理的java堆栈句柄,深入了解JVM—内存区域的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: morlet小波matlab代码,mor
- 下一篇: oracle as sydba,orac