JVM 線程棧 到 函數(shù)運行
每一個JVM線程來說啟動的時候都會創(chuàng)建一個私有的線程棧。一個jvm線程棧用來存儲棧幀,jvm線程棧和C語言中的棧很類似,它負責管理局部變量、部分運算結果,同時也參與到函數(shù)調(diào)用和函數(shù)返回的工作中。JVM規(guī)范中運行線程棧的大小可以是固定的或者是動態(tài)分配的,也可以是根據(jù)一定規(guī)則計算的。不同jvm對棧的實現(xiàn)會不同,一些可能提供給開發(fā)人員自己控制jvm線程棧初始大小的方式;對于動態(tài)分配來說也可能提供對jvm最大和最小值的設置。
當計算一個線程需要的分配的大小超出了固定值、或者設置的最大值,jvm會拋出StackOverflowError。而對于動態(tài)分配棧來說,如果內(nèi)存不能夠提供足夠的空間來滿足最小值、或者需要的值JVM會拋出 OutOfMemoryError
棧幀,可以理解成一個函數(shù)執(zhí)行的環(huán)境,它管理參數(shù)、局部變量、返回值等等。
每個棧幀都包括一個管理局部變量的數(shù)組( local variables),這個數(shù)組的單元數(shù)量在編譯成字節(jié)碼的時候就能確定了。對于32-bit 一個單位能夠存放 boolean, byte, char, short, int, float, reference,returnAddress;連續(xù)兩個單位就能夠用來存儲long 、double。局部變量數(shù)組的下標是從0開始,一般而言0位置存儲的是this,后面接著是函數(shù)的參數(shù),再是函數(shù)中出現(xiàn)的局部變量。
每個棧幀也都包括一個(LIFO)操作棧的數(shù)據(jù)結構(operand stack),它的大小同樣也可以在編譯的時候確定,創(chuàng)建的時候會是個空棧。舉個簡單的例子,來描述它公用,對于int a+b來說,先把push a 進入棧中,再樸實 b 進入入棧中,然后 同時pop 兩個值執(zhí)行iadd 指令,再將其加后的結果push入棧中完成指令。
除開以上兩個關鍵的結構,每個棧幀還有常量池( run-time constant pool)、異常拋出管理等結構。在此就不一一詳細說來了,可以參考其他資料。
再來通過一個簡單的 Demo 來說明,一個棧幀的工作。首先,我們來看這樣的一個函數(shù):
public int comp(float number1, float number2){ int result ; if(number1 < number2) result = 1; else result = 2; return result; }
其中函數(shù)內(nèi)邏輯對應的字節(jié)碼,如下:
0: fload_1 1: fload_2 2: fcmpg 3: ifge 11 6: iconst_1 7: istore_3 8: goto 1311: iconst_212: istore_313: iload_314: ireturn
對于這幾個字節(jié)碼指令稍微說明下: