官术网_书友最值得收藏!

2.4.1 局部變量表

局部變量表是棧幀的重要組成部分之一。它用于保存函數(shù)的參數(shù)及局部變量。局部變量表中的變量只在當(dāng)前函數(shù)調(diào)用中有效,當(dāng)函數(shù)調(diào)用結(jié)束后,函數(shù)棧幀銷(xiāo)毀,局部變量表也會(huì)隨之銷(xiāo)毀。

由于局部變量表在棧幀之中,因此,如果函數(shù)的參數(shù)和局部變量較多,會(huì)使局部變量表膨脹,從而每一次函數(shù)調(diào)用就會(huì)占用更多的棧空間,最終導(dǎo)致函數(shù)的嵌套調(diào)用次數(shù)減少。

【示例2-4】下面的代碼演示了這種情況,第1個(gè)recursion()函數(shù)含有3個(gè)參數(shù)和10個(gè)局部變量,因此,其局部變量表含有13個(gè)變量。而第2個(gè)recursion()函數(shù)不含有任何參數(shù)和局部變量。當(dāng)這兩個(gè)函數(shù)被嵌套調(diào)用時(shí),第2個(gè)recursion()函數(shù)可以擁有更深的調(diào)用層次。

使用參數(shù)-Xss128K執(zhí)行上述代碼中的第1個(gè)recursion()函數(shù),輸出結(jié)果如下:

使用參數(shù)-Xss128K執(zhí)行上述代碼中的第2個(gè)recursion()函數(shù),輸出結(jié)果如下:

可以看到,在相同的棧容量下,局部變量少的函數(shù)可以支持更深層次的函數(shù)調(diào)用。

使用jclasslib工具可以進(jìn)一步查看函數(shù)的局部變量信息。圖2.6顯示了第一個(gè)recursion()函數(shù)的最大局部變量表的大小為26字。因?yàn)樵摵瘮?shù)包含總共13個(gè)參數(shù)和局部變量,且都為long類型,long類型和double類型在局部變量表中需要占用2字,其他如int、short、byte、對(duì)象引用等占用1字。

圖2.6 最大局部變量表大小

說(shuō)明:字(Word)指的是計(jì)算機(jī)內(nèi)存中占據(jù)一個(gè)單獨(dú)的內(nèi)存單元編號(hào)的一組二進(jìn)制串。一般32位計(jì)算機(jī)上一個(gè)字為4個(gè)字節(jié)長(zhǎng)度。

圖2.7顯示了在Class文件中的局部變量表的內(nèi)容(這里說(shuō)的局部變量表和上述的局部變量表不同,這里指Class文件的一個(gè)屬性,而上述局部變量表指Java棧空間的一部分)。

圖2.7 Class文件中的局部變量表

可以看到,在Class文件的局部變量表中,顯示了每個(gè)局部變量的作用域范圍、所在槽位的索引(index列)、變量名(name列)和數(shù)據(jù)類型(J表示long型)。

棧幀中的局部變量表中的槽位是可以重用的,如果局部變量a超過(guò)了其作用域,那么在其作用域之后聲明的新的局部變量就很有可能會(huì)復(fù)用局部變量a的槽位,從而達(dá)到節(jié)省資源的目的。

【示例2-5】下面的代碼顯示了局部變量表槽位的復(fù)用。在localvar1()函數(shù)中,局部變量a和b都作用到了函數(shù)末尾,故b無(wú)法復(fù)用a所在的位置。而在localvar2()函數(shù)中,局部變量a在第10行時(shí)不再有效,故局部變量b可以復(fù)用a的槽位(1個(gè)字)。

圖2.8顯示了localvar1()的局部變量信息,該函數(shù)最大局部變量大小為3字,第0個(gè)槽位為函數(shù)的this引用(實(shí)例方法的第一個(gè)局部變量都是this引用),第1個(gè)槽位為變量a,第2個(gè)槽位為變量b,每個(gè)變量占1字,合計(jì)3字。

圖2.8 localvar1()的局部變量信息

圖2.9顯示了localvar2()的局部變量信息,該函數(shù)的最大局部變量表為2字,雖然和localvar1()一樣,擁有this、a、b等3個(gè)局部變量,但b復(fù)用了a的槽位(從它們都占用了第1個(gè)槽位可以知道這點(diǎn))。因此,在函數(shù)執(zhí)行中,同時(shí)存在的最大局部變量為2字。

圖2.9 localvar2()的局部變量信息

局部變量表中的變量也是重要的垃圾回收根節(jié)點(diǎn),被局部變量表中直接或間接引用的對(duì)象都是不會(huì)被回收的。因此,理解局部變量表對(duì)理解垃圾回收也有一定的幫助。

【示例2-6】下面通過(guò)一個(gè)簡(jiǎn)單的示例,展示局部變量對(duì)垃圾回收的影響。

上述代碼中,每一個(gè)localvarGcN()函數(shù)都分配了一塊6MB的堆空間,并使用局部變量引用這塊空間。

在localvarGc1()中,在申請(qǐng)空間后,立即進(jìn)行垃圾回收,很明顯,由于byte數(shù)組被變量a引用,因此無(wú)法回收這塊空間。

在localvarGc2()中,在垃圾回收前,先將變量a置為null,使byte數(shù)組失去強(qiáng)引用,故垃圾回收可以順利回收byte數(shù)組。

對(duì)于localvarGc3(),在進(jìn)行垃圾回收前,先使局部變量a失效,雖然變量a已經(jīng)離開(kāi)了作用域,但是變量a依然存在于局部變量表中,并且也指向這塊byte數(shù)組,故byte數(shù)組依然無(wú)法被回收。

對(duì)于localvarGc4(),在垃圾回收之前,不僅使變量a失效,更是聲明了變量c,使變量c復(fù)用了變量a的字,由于變量a此時(shí)被銷(xiāo)毀,故垃圾回收器可以順利回收byte數(shù)組。

對(duì)于localvarGc5(),它首先調(diào)用了localvarGc1(),很明顯,在localvarGc1()中并沒(méi)有釋放byte數(shù)組,但在localvarGc1()返回后,它的棧幀被銷(xiāo)毀,自然也包含了棧幀中的所有局部變量,故byte數(shù)組失去引用,在localvarGc5()的垃圾回收中被回收。

讀者可以使用參數(shù)-XX:+PrintGC執(zhí)行上述幾個(gè)函數(shù),在輸出的日志中,可以看到垃圾回收前后堆的大小,進(jìn)而推斷byte數(shù)組是否被回收。下面的輸出是函數(shù)localvarGc4()的運(yùn)行結(jié)果:

從日志中可以看到,堆空間從回收前的6746KB變?yōu)榛厥蘸蟮?76KB,釋放了約6MB空間。進(jìn)而可以推斷,byte數(shù)組已被回收釋放。

主站蜘蛛池模板: 枣庄市| 余姚市| 洛隆县| 庆城县| 靖宇县| 彰化市| 金寨县| 阿城市| 华蓥市| 和林格尔县| 伊春市| 多伦县| 乃东县| 鄂托克旗| 三原县| 佛学| 贺州市| 中超| 柳林县| 开化县| 龙游县| 阿城市| 旅游| 屏南县| 鲁山县| 寻乌县| 绥滨县| 界首市| 云梦县| 金乡县| 团风县| 花莲市| 绿春县| 竹溪县| 方城县| 海伦市| 尼玛县| 贵溪市| 个旧市| 衡阳市| 定州市|