日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

java进出栈_JVM函数调用:Java出入栈

發(fā)布時(shí)間:2025/3/20 java 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java进出栈_JVM函数调用:Java出入栈 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

JVM函數(shù)調(diào)用:Java出入棧

JVM函數(shù)調(diào)用:Java出入棧

目錄

局部變量表

索引復(fù)用

垃圾回收

棧數(shù)據(jù)區(qū)

棧上分配

線程作為系統(tǒng)運(yùn)算調(diào)度的最小單位,在JVM中線程的行為體現(xiàn)就是函數(shù)調(diào)用,函數(shù)調(diào)用中數(shù)據(jù)的傳遞就是通過(guò)Java棧,Java棧顧名思義有著和數(shù)據(jù)結(jié)構(gòu)中“棧”相似的屬性,后進(jìn)先出,出棧入棧,棧中保存的是棧幀,當(dāng)JVM發(fā)生函數(shù)調(diào)用時(shí),就會(huì)有一個(gè)棧幀被壓入Java棧,當(dāng)函數(shù)調(diào)用結(jié)束后,再?gòu)臈V袕棾鰲?#xff0c;當(dāng)前正在執(zhí)行的函數(shù)其對(duì)應(yīng)的棧幀位于棧頂處,且保存有當(dāng)前函數(shù)的局部變量表和棧數(shù)據(jù)區(qū)(保存一些中間結(jié)果等數(shù)據(jù))。在函數(shù)返回,也就是有棧幀要從Java棧中彈出時(shí),正常的情況是函數(shù)通過(guò)return返回,此時(shí)棧幀正常彈出,如果函數(shù)調(diào)用出現(xiàn)問(wèn)題無(wú)法正常返回,則拋出異常,舉個(gè)例子:我們每一次函數(shù)調(diào)用時(shí)都會(huì)對(duì)Java棧進(jìn)行入棧操作,棧空間是一定的,隨著不斷入棧操作,例如遞歸函數(shù)調(diào)用,??臻g變得越來(lái)越小,最后達(dá)到最大可用深度時(shí),就會(huì)拋出棧溢出異常,所以有時(shí)我們遞歸函數(shù)調(diào)用過(guò)程中出現(xiàn)的“StackOverflowError”,就是棧空間因?yàn)槟承┰虮徽紳M了導(dǎo)致的。

局部變量表

函數(shù)對(duì)應(yīng)的棧幀中有一個(gè)局部變量表,里面保存了調(diào)用函數(shù)的局部變量,參數(shù)等,這些參數(shù)和變量是跟著函數(shù)走的,只在當(dāng)前函數(shù)調(diào)用中有效,函數(shù)調(diào)用結(jié)束后,棧幀就會(huì)彈出Java棧,局部變量表也就隨之被銷毀。來(lái)看一個(gè)簡(jiǎn)單的例子:

draw()方法中有3個(gè)入?yún)⒑?個(gè)局部變量,它們都是int數(shù)據(jù)類型,一個(gè)占用24個(gè)字節(jié)內(nèi)存空間,在32位操作系統(tǒng)中每4個(gè)字節(jié)為一個(gè)字,所以在局部變量表中,函數(shù)draw()的局部變量一共占6個(gè)字。

從上圖可以看到,Maximum local variables表示最大局部變量表大小為6個(gè)字。

索引復(fù)用

詳細(xì)點(diǎn)開(kāi)draw()函數(shù)中的局部變量表,可以看到一些更詳細(xì)的屬性,例如每一個(gè)變量的索引index,變量名name和數(shù)據(jù)類型descriptor等。

局部變量表中的索引是可以進(jìn)行復(fù)用的,以次來(lái)節(jié)省Java棧空間,具體的復(fù)用方式是這樣,假設(shè)我們定義了一個(gè)局部變量i,當(dāng)程序運(yùn)行到i離開(kāi)其作用域后,再定義的其他變量可能就會(huì)復(fù)用i變量的索引,具體看個(gè)例子:

package cell;

public class Cell {

private static boolean tag = true;

public void example1() {

int i = 9527;

System.out.println("i = " + i);

int s = 9527;

}

public void example2() {

if (tag) {

int i = 9527;

System.out.println("i = " + i);

}

int s = 9527;

}

}

程序中有兩個(gè)方法,example1()中定義的變量i和變量s作用域都是一樣的,直到example1()方法的結(jié)束,所以兩者的索引沒(méi)有辦法復(fù)用。example2()方法中,局部變量i在第16行后就離開(kāi)了作用域范圍,那么后續(xù)定義的局部變量s可以復(fù)用它的索引,從局部變量表里看,的確是這樣的:

可以看到,方法example1()中局部變量i和s的所有不同,分別是1和2,而到了方法example2()中,局部變量i和s的所有都一樣是1。

垃圾回收

索引復(fù)用有時(shí)也會(huì)對(duì)JVM的垃圾回收產(chǎn)生影響,例如某一個(gè)變量i,雖然離開(kāi)了自己的作用域,但是它之前指向了某一對(duì)象,使得變量i仍然存在于局部變量表中,導(dǎo)致變量i指向的對(duì)象無(wú)法被回收如果變量i指向了某一對(duì)象,i離開(kāi)了作用域后,其索引被后面定義的局部變量所服用了,那么變量i也會(huì)被銷毀掉,其指向的對(duì)象也就能被正常GC,看一個(gè)例子:

public void example1() {

byte[] buffer = new byte[2*1024*1024];

System.gc();

}

public void example2() {

byte[] buffer = new byte[2*1024*1024];

buffer = null;

System.gc();

}

public void example3() {

boolean tag = true;

if (tag) {

byte[] buffer = new byte[2*1024*1024];

}

System.gc();

}

public void example4() {

boolean tag = true;

if (tag) {

byte[] buffer = new byte[2*1024*1024];

}

int i = 9527;

System.gc();

}

上面的程序中,4個(gè)方法,第一個(gè)example1()中,首先定義byte數(shù)組,申請(qǐng)2MB大小的空間,之后立刻進(jìn)行GC,回收該數(shù)組對(duì)象,但是注意,此時(shí)因?yàn)榫植孔兞縝uffer強(qiáng)引用了這塊內(nèi)存空間,所以gc暫時(shí)無(wú)法對(duì)其進(jìn)行回收。到example2(),在為byte數(shù)組申請(qǐng)空間,并用局部變量buffer引用這塊區(qū)域后,顯示將buffer置為null,這樣buffer就不會(huì)強(qiáng)引用這塊內(nèi)存空間,之后再進(jìn)行GC就可以成功回收。example3()中,我們讓局部變量buffer作用在if語(yǔ)句中,當(dāng)if語(yǔ)句結(jié)束,buffer離開(kāi)了它的作用域后,我們?cè)龠M(jìn)行GC,此時(shí)因?yàn)樽兞縝uffer還存在于局部變量表中,所以它的引用還是有效的,GC無(wú)法對(duì)其引用的空間進(jìn)行回收,解決的辦法來(lái)看example4()方法。在example4()中,buffer離開(kāi)其作用域后,我們?cè)俾暶髁硪粋€(gè)局部變量i,讓它來(lái)復(fù)用變量buffer的索引,,這樣buffer變量就真正被替代銷毀了,并且沒(méi)有其他變量引用這片內(nèi)存區(qū)域,之后再進(jìn)行GC,可以成功進(jìn)行回收。

棧數(shù)據(jù)區(qū)

Java棧中局部變量表上面簡(jiǎn)單總結(jié)了一下,除了局部便變量表,還有棧數(shù)據(jù)區(qū),分為虛擬機(jī)棧和本地方法棧,虛擬機(jī)棧存放就是棧幀,Java方法運(yùn)行時(shí)所需要的數(shù)據(jù),本地方法棧存放的是JVM調(diào)用的本地方法。

棧上分配

上面說(shuō)的棧數(shù)據(jù)區(qū)中,虛擬機(jī)棧和本地方法棧都是線程獨(dú)占的,對(duì)于一些線程私有的,不能被其他線程訪問(wèn)的對(duì)象,JVM可以把它們分配在棧上,這樣當(dāng)函數(shù)調(diào)用結(jié)束后就會(huì)自行出棧銷毀,不需要GC來(lái)進(jìn)行回收,好處就是提高了性能。

private static People p;

public static void initPeople() {

p = new People();

p.name = "Alex";

p.age = 20;

}

上面代碼中People類對(duì)象p是一個(gè)逃逸的對(duì)象,因?yàn)樗穷惖某蓡T變量,可能會(huì)被其他線程訪問(wèn)到,所以虛擬機(jī)會(huì)把它分配到堆上,而不是線程私有的棧數(shù)據(jù)區(qū)中。如果我們把它改成非逃逸的對(duì)象:

public static void initPeople() {

People p = new People();

p.name = "Alex";

p.age = 20;

}

把對(duì)象p改成局部變量的方式,且initPeople()方法也沒(méi)有將其返回出去,那么該對(duì)象p沒(méi)有發(fā)生逃逸,虛擬機(jī)就會(huì)將它分配到棧數(shù)據(jù)區(qū)中。

JVM函數(shù)調(diào)用:Java出入棧相關(guān)教程

總結(jié)

以上是生活随笔為你收集整理的java进出栈_JVM函数调用:Java出入栈的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。