更新時(shí)間:2020-10-09 17:30:54 來源:動力節(jié)點(diǎn) 瀏覽1710次
JVM棧,全稱為Java 虛擬機(jī)棧,線程私有,生命周期和線程一致。描述的是 Java 方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行時(shí)都會床創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。每一個(gè)方法從調(diào)用直至執(zhí)行結(jié)束,就對應(yīng)著一個(gè)棧幀從虛擬機(jī)棧中入棧到出棧的過程。
當(dāng)程序開始執(zhí)行時(shí),由于只有一個(gè)main線程,因而JVM只需要為main線程分配好棧區(qū)的內(nèi)存(話句話說如果有多個(gè)線程,自然就會有多個(gè)棧區(qū),并且為各自線程私有)。main繼續(xù)執(zhí)行,就會遇到main()方法,遇到之后呢!JVM又會在棧區(qū)當(dāng)中再劃出一個(gè)小塊來存放main()方法執(zhí)行過程的數(shù)據(jù),這一小塊區(qū)域也就是棧幀。main()方法執(zhí)行過程中又有一個(gè)add()方法出現(xiàn)了,同樣地,JVM又會再為add()分配一個(gè)棧幀,同時(shí)壓入到棧區(qū),以后再遇到其他方法也是如此。當(dāng)然,方法在執(zhí)行完成之后,便會彈出并釋放內(nèi)存,當(dāng)線程中棧區(qū)的所有方法都返回之后,程序也就算是執(zhí)行完畢了。
當(dāng)我們扯開棧區(qū),撕開棧幀,一不小心,局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口......嘩啦啦地散落一地。

撿起add()棧幀的局部變量表和操作數(shù)棧就可以看到這樣一個(gè)畫面,在執(zhí)行例子中add()方法中的三行代碼時(shí),局部變量表和操作數(shù)棧的一個(gè)變化過程:首先,執(zhí)行int a = 3;局部變量表中會分配出一個(gè)int區(qū)域,表示為a;同時(shí)iconst命令使得操作數(shù)棧中壓入了常量3,然后再由istore命令將3彈出,賦值給局部變量表中a。同樣,int b = 4; 這一行代碼也是如此。然后,int c = a + b;從右往左開始,先執(zhí)行a + b,也就是iload命令從局部變量中取出a、b對應(yīng)的值,再將iadd后的值push進(jìn)操作數(shù)棧中,剩下的便是int c = 7的操作了。
通過上面的例子,就很容易明白;局部變量表,顧名思義就是存放每個(gè)方法中的局部變量(即編譯器可知的各種基本類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference 類型)和 returnAddress 類型(指向了一條字節(jié)碼指令的地址))所在處,如圖中的a、b。操作數(shù)棧,也就是存放的就是方法當(dāng)中的各種操作數(shù)的臨時(shí)空間,又如栗子中的3、4。
動態(tài)鏈接:Class文件的常量池中存在有大量的符號引用,字節(jié)碼中的方法調(diào)用指令就以指向常量池的引用作為參數(shù),而將部分符號引用在運(yùn)行期間轉(zhuǎn)化為直接引用,這種轉(zhuǎn)化即為動態(tài)鏈接。這個(gè)解釋當(dāng)中會涉及到許多概念,比如常量池、符號引用等,要想理解這些概念,就需要去了解class文件的結(jié)構(gòu),內(nèi)容太多就不在這里詳細(xì)描述了。
方法出口:簡單來說,就是用于標(biāo)記當(dāng)前方法執(zhí)行完成之后,應(yīng)該返回到下一條指令執(zhí)行位置。比如就上面的栗子而言,add()在執(zhí)行完畢之后,就應(yīng)該返回到e1.add()之后繼續(xù)執(zhí)行main()后面的代碼。
上面就是對JVM棧的全面講解,還沒有完全弄懂的小伙伴可以通過觀看本站的Java零基礎(chǔ)教程學(xué)習(xí)更多的JVM知識。

初級 202925

初級 203221

初級 202629

初級 203743