Java開發中,每當咱們在程序中使用new生成一個對象,對象的引用存放在棧裏,而對象是存放在堆裏的。能夠看出棧在Java核心的重要位置。今天咱們就繼續深刻Java核心這個系列,爲您介紹Java中的棧、局部變量及其之間的關係。數組
深刻Java核心:Java內存分配原理精講 探祕Java垃圾回收機制 Java中多態的實現機制 多線程
Java中的棧this
每當啓用一個線程時,JVM就爲他分配一個Java棧,棧是以幀爲單位保存當前線程的運行狀態。某個線程正在執行的方法稱爲當前方法,當前方法使用的棧幀稱爲當前幀,當前方法所屬的類稱爲當前類,當前類的常量池稱爲當前常量池。當線程執行一個方法時,它會跟蹤當前常量池。spa
每當線程調用一個Java方法時,JVM就會在該線程對應的棧中壓入一個幀,這個幀天然就成了當前幀。當執行這個方法時,它使用這個幀來存儲參數、局部變量、中間運算結果等等。線程
Java棧上的全部數據都是私有的。任何線程都不能訪問另外一個線程的棧數據。因此咱們不用考慮多線程狀況下棧數據訪問同步的狀況。指針
像方法區和堆同樣,Java棧和幀在內存中也沒必要是連續的,幀能夠分佈在連續的棧裏,也能夠分佈在堆裏code
Java棧的組成元素——棧幀htm
棧幀由三部分組成:局部變量區、操做數棧、幀數據區。局部變量區和操做數棧的大小要視對應的方法而定,他們是按字長計算的。但調用一個方法時,它從類型信息中獲得此方法局部變量區和操做數棧大小,並據此分配棧內存,而後壓入Java棧。對象
局部變量區 局部變量區被組織爲以一個字長爲單位、從0開始計數的數組,類型爲short、byte和char的值在存入數組前要被轉換成int值,而long和double在數組中佔據連續的兩項,在訪問局部變量中的long或double時,只需取出連續兩項的第一項的索引值便可,如某個long值在局部變量區中佔據的索引時三、4項,取值時,指令只需取索引爲3的long值便可。blog
下面就看個例子,好讓你們對局部變量區有更深入的認識。這個圖來自《深刻JVM》:
- public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) {
- return ;
- }
- public int runInstanceMethod(char c,double d,short s,boolean b) {
- return ;
- }
上面代碼片的方法參數和局部變量在局部變量區中的存儲結構以下圖:
上面這個圖沒什麼好說的,你們看看就會懂。可是,在這個圖裏,有一點須要注意:
runInstanceMethod的局部變量區第一項是個reference(引用),它指定的就是對象自己的引用,也就是咱們經常使用的this,可是在runClassMethod方法中,沒這個引用,那是由於runClassMethod是個靜態方法。
操做數棧和局部變量區同樣,操做數棧也被組織成一個以字長爲單位的數組。但和前者不一樣的是,它不是經過索引來訪問的,而是經過入棧和出棧來訪問的。可把操做數棧理解爲存儲計算時,臨時數據的存儲區域。下面咱們經過一段簡短的程序片斷外加一幅圖片來了解下操做數棧的做用。
int a = 100;
int b = 98;
int c = a+b;
從圖中能夠得出:操做數棧其實就是個臨時數據存儲區域,它是經過入棧和出棧來進行操做的。
幀數據區除了局部變量區和操做數棧外,Java棧幀還須要一些數據來支持常量池解析、正常方法返回以及異常派發機制。這些數據都保存在Java棧幀的幀數據區中。
當JVM執行到須要常量池數據的指令時,它都會經過幀數據區中指向常量池的指針來訪問它。
除了處理常量池解析外,幀裏的數據還要處理Java方法的正常結束和異常終止。若是是經過return正常結束,則當前棧幀從Java棧中彈出,恢復發起調用的方法的棧。若是方法又返回值,JVM會把返回值壓入到發起調用方法的操做數棧。
爲了處理Java方法中的異常狀況,幀數據區還必須保存一個對此方法異常引用表的引用。當異常拋出時,JVM給catch塊中的代碼。若是沒發現,方法當即終止,而後JVM用幀區數據的信息恢復發起調用的方法的幀。而後再發起調用方法的上下文從新拋出一樣的異常。
棧的整個結構
在前面就描述過:棧是由棧幀組成,每當線程調用一個Java方法時,JVM就會在該線程對應的棧中壓入一個幀,而幀是由局部變量區、操做數棧和幀數據區組成。那在一個代碼塊中,棧究竟是什麼形式呢?下面是我從《深刻JVM》中摘抄的一個例子,你們能夠看看:
代碼片斷:
執行過程當中的三個快照:
上面所給的圖,只想說明兩件事情,咱們也可用此來理解Java中的棧:
一、只有在調用一個方法時,才爲當前棧分配一個幀,而後將該幀壓入棧。
二、幀中存儲了對應方法的局部數據,方法執行完,對應的幀則從棧中彈出,並把返回結果存儲在調用方法的幀的操做數棧中。