JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)域

C語(yǔ)言的陰影

還記得剛進(jìn)大學(xué)的時(shí)候,以為這個(gè)世界上最難學(xué)的不過(guò)C語(yǔ)言了。盡管后來(lái)陸續(xù)學(xué)了很多的更難的課程,盡管慢慢掌握了計(jì)算機(jī)的很多原理之后,回頭來(lái)看C語(yǔ)言,似乎沒那么難理解,可當(dāng)年初學(xué)C語(yǔ)言時(shí)的“陰影”,這么多年來(lái),一直沒有散去。

我經(jīng)常還能想到幾年前,懶散的趴在逸夫教學(xué)樓F1教室最后一排的座位上,聽蘭書敏老師講著“戲院”(C語(yǔ)言)的場(chǎng)景。蘭老師問(wèn)到:“你們?cè)趺炊疾豢月暎康降资悄睦锫牪欢??”老師,學(xué)生當(dāng)時(shí)真是哪哪兒都沒聽懂啊。

 

身在Java,心在C(Java大神勿噴,C對(duì)我來(lái)說(shuō),真是一種情懷)

沒想到,工作一年多的時(shí)間里,用的最多的語(yǔ)言不是對(duì)我影響最大的C,而是大學(xué)畢業(yè)之后現(xiàn)學(xué)現(xiàn)賣的Java。所以我對(duì)C和Java都算有一點(diǎn)了解。

 

一條有意思的Java面試題

前幾天在搜索一個(gè)問(wèn)題的解決方案時(shí),偶然看到一個(gè)Java面試題,覺得網(wǎng)上絕大多數(shù)解釋,有些浮于表面。而真神們又不屑于解釋這些無(wú)聊的問(wèn)題,所以覺得有必要站在一個(gè)“雙修(殘廢)”者的角度,談?wù)勥@個(gè)問(wèn)題。

 

Java內(nèi)存分配

在解釋這個(gè)問(wèn)題之前,我想簡(jiǎn)單的記錄一下Java虛擬機(jī)對(duì)內(nèi)存的分配管理。

網(wǎng)上有很多關(guān)于Java內(nèi)存管理的講解,但不知道為什么,大多數(shù)作者并沒有系統(tǒng)的講解,有些過(guò)于散碎。

我們先來(lái)看看這張圖(我不會(huì)畫圖,畫的太丑,各位受累受累了)。簡(jiǎn)單的說(shuō),Java運(yùn)行時(shí)內(nèi)存區(qū)域,就由上面幾部分構(gòu)成。青綠色標(biāo)記的,是每個(gè)線程私有的內(nèi)存區(qū)域,其他的為線程共享的內(nèi)存區(qū)域。我們先簡(jiǎn)單的依次說(shuō)明每個(gè)部分是用來(lái)存什么的,最后再用一個(gè)簡(jiǎn)單的例子,將各個(gè)部分結(jié)合起來(lái)簡(jiǎn)單介紹其內(nèi)存分配的基本過(guò)程。

首先,程序計(jì)數(shù)器(pc)。這個(gè)東西對(duì)于很多開發(fā)者來(lái)說(shuō),再熟悉不過(guò)了,盡管不同領(lǐng)域的pc,具體用法上存在一些小小的差異,但總的來(lái)說(shuō),pc是用來(lái)記錄程序運(yùn)行到哪里了,下一步又該執(zhí)行哪一步操作。pc占據(jù)的內(nèi)存是線程級(jí)的,即隨線程的創(chuàng)建而產(chǎn)生,隨線程的銷毀而銷毀(被回收)。

其次JVM棧和本地方法棧。這兩個(gè)棧在存儲(chǔ)結(jié)構(gòu)上,基本相同,以至于很多的JVM產(chǎn)商,將二者合而為一。JVM棧,顧名思義,是用來(lái)存儲(chǔ)Java方法運(yùn)行過(guò)程中使用的棧數(shù)據(jù),本地方法棧就是用來(lái)存儲(chǔ)本地方法執(zhí)行過(guò)程中的棧數(shù)據(jù)。棧中存儲(chǔ)的數(shù)據(jù),是一種被稱為“棧幀”的東西。棧幀主要包括:局部變量表和操作數(shù)棧。棧幀的入棧和出棧,分別意味著一個(gè)方法的執(zhí)行與結(jié)束。

接著,我們來(lái)看看方法區(qū)。方法區(qū)主要是用來(lái)存類型數(shù)據(jù)的,與類型相關(guān)的東西,比如常量,靜態(tài)變量,編譯后的代碼等,基本都存儲(chǔ)在這一區(qū)域。而因?yàn)椤盁o(wú)用類”的判斷條件非??量蹋ㄓ腥c(diǎn),第一,該類無(wú)可達(dá)對(duì)象,第二,該類的ClassLoader已被回收,第三,該類的Class對(duì)象無(wú)引用),這個(gè)區(qū)域存儲(chǔ)的內(nèi)容很難會(huì)被回收,所以你可能會(huì)在很多地方看到“永久代”一詞,其實(shí)說(shuō)的主要也就是這個(gè)方法區(qū)。方法區(qū)中,有個(gè)特殊的區(qū)域,被劃分(邏輯劃分,不一定為物理劃分)出來(lái),即“運(yùn)行時(shí)常量池”。運(yùn)行時(shí)常量池,保存著字面量,符號(hào)引用等。方法區(qū)是線程共享的,隨JVM啟動(dòng)而創(chuàng)建,JVM退出而銷毀。

最后,是這個(gè)堆。堆,在很多領(lǐng)域也有用到。在Java中,堆,是用來(lái)存儲(chǔ)對(duì)象的相關(guān)內(nèi)容,包括對(duì)象的對(duì)象頭和實(shí)例數(shù)據(jù)(數(shù)組對(duì)象還有一個(gè)數(shù)組的長(zhǎng)度)。不同的JVM實(shí)現(xiàn),對(duì)象可能還包括類型指針(指向?qū)ο笏鶎俚念愋托?