JVM 運行時數(shù)據(jù)區(qū)域
C語言的陰影
還記得剛進大學的時候,以為這個世界上最難學的不過C語言了。盡管后來陸續(xù)學了很多的更難的課程,盡管慢慢掌握了計算機的很多原理之后,回頭來看C語言,似乎沒那么難理解,可當年初學C語言時的“陰影”,這么多年來,一直沒有散去。
我經(jīng)常還能想到幾年前,懶散的趴在逸夫教學樓F1教室最后一排的座位上,聽蘭書敏老師講著“戲院”(C語言)的場景。蘭老師問到:“你們怎么都不吭聲?到底是哪里聽不懂?”老師,學生當時真是哪哪兒都沒聽懂啊。
身在Java,心在C(Java大神勿噴,C對我來說,真是一種情懷)
沒想到,工作一年多的時間里,用的最多的語言不是對我影響最大的C,而是大學畢業(yè)之后現(xiàn)學現(xiàn)賣的Java。所以我對C和Java都算有一點了解。
一條有意思的Java面試題
前幾天在搜索一個問題的解決方案時,偶然看到一個Java面試題,覺得網(wǎng)上絕大多數(shù)解釋,有些浮于表面。而真神們又不屑于解釋這些無聊的問題,所以覺得有必要站在一個“雙修(殘廢)”者的角度,談談這個問題。
Java內存分配
在解釋這個問題之前,我想簡單的記錄一下Java虛擬機對內存的分配管理。
網(wǎng)上有很多關于Java內存管理的講解,但不知道為什么,大多數(shù)作者并沒有系統(tǒng)的講解,有些過于散碎。
我們先來看看這張圖(我不會畫圖,畫的太丑,各位受累受累了)。簡單的說,Java運行時內存區(qū)域,就由上面幾部分構成。青綠色標記的,是每個線程私有的內存區(qū)域,其他的為線程共享的內存區(qū)域。我們先簡單的依次說明每個部分是用來存什么的,最后再用一個簡單的例子,將各個部分結合起來簡單介紹其內存分配的基本過程。
首先,程序計數(shù)器(pc)。這個東西對于很多開發(fā)者來說,再熟悉不過了,盡管不同領域的pc,具體用法上存在一些小小的差異,但總的來說,pc是用來記錄程序運行到哪里了,下一步又該執(zhí)行哪一步操作。pc占據(jù)的內存是線程級的,即隨線程的創(chuàng)建而產生,隨線程的銷毀而銷毀(被回收)。
其次JVM棧和本地方法棧。這兩個棧在存儲結構上,基本相同,以至于很多的JVM產商,將二者合而為一。JVM棧,顧名思義,是用來存儲Java方法運行過程中使用的棧數(shù)據(jù),本地方法棧就是用來存儲本地方法執(zhí)行過程中的棧數(shù)據(jù)。棧中存儲的數(shù)據(jù),是一種被稱為“棧幀”的東西。棧幀主要包括:局部變量表和操作數(shù)棧。棧幀的入棧和出棧,分別意味著一個方法的執(zhí)行與結束。
接著,我們來看看方法區(qū)。方法區(qū)主要是用來存類型數(shù)據(jù)的,與類型相關的東西,比如常量,靜態(tài)變量,編譯后的代碼等,基本都存儲在這一區(qū)域。而因為“無用類”的判斷條件非常苛刻(有三點,第一,該類無可達對象,第二,該類的ClassLoader已被回收,第三,該類的Class對象無引用),這個區(qū)域存儲的內容很難會被回收,所以你可能會在很多地方看到“永久代”一詞,其實說的主要也就是這個方法區(qū)。方法區(qū)中,有個特殊的區(qū)域,被劃分(邏輯劃分,不一定為物理劃分)出來,即“運行時常量池”。運行時常量池,保存著字面量,符號引用等。方法區(qū)是線程共享的,隨JVM啟動而創(chuàng)建,JVM退出而銷毀。
最后,是這個堆。堆,在很多領域也有用到。在Java中,堆,是用來存儲對象的相關內容,包括對象的對象頭和實例數(shù)據(jù)(數(shù)組對象還有一個數(shù)組的長度)。不同的JVM實現(xiàn),對象可能還包括類型指針(指向對象所屬的類型信