JAVA存儲機(jī)制(棧、堆、方法區(qū)詳解)
JAVA存儲機(jī)制(棧、堆、方法區(qū)詳解) - SegmentFault 思否
一、JAVA的六種存儲地址
- 寄存器 register
位于處理器內(nèi)部,是最快的儲存器,但是數(shù)量極其有限。由編譯器根據(jù)需求進(jìn)行分配,不能由代碼控制,對于開發(fā)者來說是 無感知 的。 - 堆棧 stack
位于RAM中,堆棧指針下移分配新的內(nèi)存,上移釋放內(nèi)存。創(chuàng)建程序的時候,編譯器必須知道存儲在堆棧中所有數(shù)據(jù)的確切大小和生命周期。某些JAVA數(shù)據(jù)存儲在堆棧中——特別是對象引用,但是JAVA對象不存儲其中。 - 堆 heap
位于RAM中,相比堆棧的優(yōu)勢是不需要知道從堆里分配多少存儲空間以及存活時間。JAVA對象存儲在這里。 - 靜態(tài)存儲 static
這里的“靜態(tài)”是指“在固定的位置”。靜態(tài)存儲里存放程序運(yùn)行時一直存在的數(shù)據(jù)。你可用關(guān)鍵字static來標(biāo)識一個對象的特定元素是靜態(tài)的 - 常量存儲 constant
常量值通常直接存放在程序代碼內(nèi)部,這樣做是安全的,因為它們永遠(yuǎn)不會被改變。有時,在嵌入式系統(tǒng)中,常量本身會和其他部分分割離開,所以在這種情況下,可以選擇將其放在ROM中。 - 非RAM存儲
如果數(shù)據(jù)完全存活于程序之外,那么它可以不受程序的任何控制,在程序沒有運(yùn)行時也可以存在。
二、棧、堆、方法區(qū)存儲的內(nèi)容
-
堆區(qū):
- 存儲的全部是對象,每個對象都包含一個與之對應(yīng)的class的信息。(class的目的是得到操作指令)
- jvm只有一個堆區(qū)(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身 。
背景知識 --- 引用<<Java面向?qū)ο缶幊?gt;>
10.2.1 類的加載
類的加載是指把類的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,把它存放在運(yùn)行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在堆區(qū)創(chuàng)建一個java.lang.Class對象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。
類加載的最終產(chǎn)品是位于運(yùn)行時數(shù)據(jù)區(qū)的堆區(qū)的Class對象。Class對象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu),并且向Java程序提供了訪問類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口,參見圖 10 -2。
圖 10-2 Class對象是Java程序與在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口
-
棧區(qū):
- 每個線程包含一個獨(dú)立的方法調(diào)用棧(method invocation stack),簡稱方法棧。棧中只保存基礎(chǔ)數(shù)據(jù)類型的值和對象以及基礎(chǔ)數(shù)據(jù)的引用
- 每個棧中的數(shù)據(jù)(基礎(chǔ)數(shù)據(jù)類型和對象引用)都是私有的,其他棧不能訪問。
- 棧分為3個部分:基本類型變量區(qū)、執(zhí)行環(huán)境上下文、操作指令區(qū)(存放操作指令)。
-
方法區(qū):
- 又叫靜態(tài)區(qū),跟堆一樣,被所有的線程共享。方法區(qū)包含所有的class和static變量。
- 方法區(qū)中包含的都是在整個程序中永遠(yuǎn)唯一的元素,如class,static變量。

三、JAVA 基本數(shù)據(jù)類型的存儲
基本類型一共有8種,即int, short, long, byte, float, double, boolean, char。這種類型的定義是通過int a = 3 的形式來定義的,稱為自動變量。值得注意的是 自動變量存的是字面值,不是類的實例。
例如 int a = 3,這里的a是一個指向int類型的引用,指向3這個字面值,整個過程種沒有類的存在。字面值的數(shù)據(jù)大小可知,生存期可知(定義在程序塊中,程序塊退出后,字段值就消失了),存在棧中。
棧有一個很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享
假設(shè)我們同時定義 int a = 3; int b = 3;
編譯器先處理int a = 3;首先它會在棧中創(chuàng)建一個變量為a的引用,然后查找有沒有字面值為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然后將a指向3的地址。接著處理int b = 3;在創(chuàng)建完b的引用變量后,由于在棧中已經(jīng)有3這個字面值,便將b直接指向3的地址。這樣,就出現(xiàn)了a與b同時均指向3的情況。
特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一個對象,如果一個對象引用變量修改了這個對象的內(nèi)部狀態(tài),那么另一個對象引用變量也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導(dǎo)致另一個指向此字面值的引用的值也跟著改變的情況。
如上例,我們定義完a與 b的值后,再令a=4;那么,b不會等于4,還是等于3。在編譯器內(nèi)部,遇到a=4;時,它就會重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經(jīng)有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。

浙公網(wǎng)安備 33010602011771號