java中數(shù)據(jù)的5種存儲位置(堆與棧)

任何語言所編寫的程序,其中的各類型的數(shù)據(jù)都需要一個存儲位置,Java中數(shù)據(jù)的存儲位置分為以下5種:

1.寄存器

最快的存儲區(qū),位于處理器內(nèi)部抚恒,但是數(shù)量極其有限。所以寄存器根據(jù)需求進行自動分配络拌,無法直接人為控制俭驮。

2.棧內(nèi)存

位于RAM當中,通過堆棧指針可以從處理器獲得直接支持。堆棧指針向下移動混萝,則分配新的內(nèi)存遗遵;向上移動,則釋放那些內(nèi)存逸嘀。這種存儲方式速度僅次于寄存器车要。

(常用于存放對象引用和基本數(shù)據(jù)類型,而不用于存儲對象)

3.堆內(nèi)存

一種通用的內(nèi)存池崭倘,也位于RAM當中翼岁。其中存放的數(shù)據(jù)由JVM自動進行管理。

堆相對于棧的好處來說:編譯器不需要知道存儲的數(shù)據(jù)在堆里存活多長司光。當需要一個對象時琅坡,使用new寫一行代碼,當執(zhí)行這行代碼時残家,會自動在堆里進行存儲分配榆俺。同時,因為以上原因坞淮,用堆進行數(shù)據(jù)的存儲分配和清理茴晋,需要花費更多的時間。

4.常量池

常量(字符串常量和基本類型常量)通常直接存儲在程序代碼內(nèi)部(常量池)回窘。這樣做是安全的诺擅,因為它們的值在初始化時就已經(jīng)被確定,并不會被改變啡直。常量池在java用于保存在編譯期已確定的烁涌,已編譯的class文件中的一份數(shù)據(jù)。它包括了關(guān)于類付枫,方法烹玉,接口等中的常量,也包括字符串常量阐滩,如String

s = "java"這種申明方式

5.非RAM存儲區(qū)

如果數(shù)據(jù)完全存活于程序之外二打,那么它可以不受程序的任何控制,在程序沒有運行時也可以存在掂榔。其中兩個基本的例子是:流對象和持久化對象继效。

Java中數(shù)據(jù)的存儲分為以上5種方式,但在實際中最常談起的是:堆內(nèi)存存儲 與 棧內(nèi)存存儲装获。

我們可以聯(lián)系著二者來分析這兩種不同的存儲方式瑞信,更利于我們理解:

首先,它們有一定的相同之處:

堆與棧都是用于程序中的數(shù)據(jù)在RAM(內(nèi)存)上的存儲區(qū)域穴豫。并且Java會自動地管理堆和棧凡简,不能人為去直接設(shè)置逼友。

其次,更關(guān)鍵的在于它們的不同之處:

1.存儲數(shù)據(jù)類型:棧內(nèi)存中存放局部變量(基本數(shù)據(jù)類型和對象引用)秤涩,而堆內(nèi)存用于存放對象(實體)帜乞。

2.存儲速度:就存儲速度而言,棧內(nèi)存的存儲分配與清理速度更快于堆筐眷,并且棧內(nèi)存的存儲速度僅次于直接位于處理器當中的寄存器黎烈。

3.靈活性:就靈活性而言,由于棧內(nèi)存與堆內(nèi)存存儲機制的不同匀谣,堆內(nèi)存靈活性更優(yōu)于棧內(nèi)存照棋。

這樣兩種存儲方式的不同之處,也是由于它們自身的存儲機制所造成的武翎。所以為了理解它們烈炭,首先我們應該弄清楚它們分別的存儲原理和機制,在Java中:

— 棧內(nèi)存被要求存放在其中的數(shù)據(jù)的大小后频、生命周期必須是已經(jīng)確定的梳庆;

— 堆內(nèi)存可以被虛擬機動態(tài)的分配內(nèi)存大小暖途,無需事先告訴編譯器的數(shù)據(jù)的大小卑惜、生命周期等相關(guān)信息。

接下來便可以進行分析:

棧內(nèi)存和堆內(nèi)存的存儲數(shù)據(jù)類型為何不同驻售?

我們知道在Java中露久,變量的類型通常分為:基本數(shù)據(jù)類型變量和對象引用變量。

首先欺栗,8種基本數(shù)據(jù)類型中的數(shù)字類型實際上都是存儲的一組位數(shù)(所占bit位)不同的二進制數(shù)據(jù)毫痕;除此之外,布爾型只有true和false兩種可能值迟几。

其次消请,對象引用變量存儲的,實際是其所關(guān)聯(lián)(指向)對象在內(nèi)存中的內(nèi)存地址类腮,而內(nèi)存地址實際上也是一串二進制的數(shù)據(jù)臊泰。

所以,局部變量的大小是可以被確定的蚜枢;

接下來缸逃,java中,局部變量會在其自身所屬方法(或代碼塊)執(zhí)行完畢后厂抽,被自動釋放需频。

所以局部變量的生命周期也是可以被確定的。

那么筷凤,既然局部變量的大小和生命周期都可以被確定昭殉,完全符合棧內(nèi)存的存儲特點。自然,局部變量被存放在棧內(nèi)存中挪丢。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

而Java中使用關(guān)鍵字new通過調(diào)用類的構(gòu)造函數(shù)莽鸭,從而得到該類的對象。

對象類型數(shù)據(jù)在程序編譯期吃靠,并不會在內(nèi)存中進行創(chuàng)建和存儲工作硫眨;而是在程序運行期,才根據(jù)需要進行動態(tài)的創(chuàng)建和存儲巢块。

也就是說礁阁,在程序運行之前,我們永遠不能確定這個對象的內(nèi)容族奢、大小姥闭、生命周期。自然越走,對象由堆內(nèi)存進行存儲管理棚品。

為什么棧內(nèi)存的速度高于堆內(nèi)存?

我個人是這樣理解的:

1.棧中數(shù)據(jù)大小和生命周期確定廊敌;堆中不確定铜跑。

2.說到大小,棧中存放的局部變量(8種基本數(shù)據(jù)類型和對象引用)實際值基本都是一串二進制數(shù)據(jù)骡澈,所以數(shù)據(jù)很小锅纺。而堆中存放的對象類型數(shù)據(jù)更大。

3.說到生命周期肋殴,棧中的數(shù)據(jù)在其所屬方法或代碼塊執(zhí)行結(jié)束后囤锉,就被釋放;而堆中的數(shù)據(jù)由垃圾回收機制進行管理护锤,無法確定合適會被回收釋放官地。

那么,一進行比較烙懦,很明顯的可以預見到:自身信息(大小和生命周期)確定驱入,數(shù)據(jù)大小更小的數(shù)據(jù)被處理起來肯定更加快捷,所以棧的存儲管理速度優(yōu)于堆修陡。

這就好比沧侥,明天要進行兩場考試:

第一場考試的試卷共有20道題,并且老師提前告訴了你所有題目魄鸦,你進行了復習宴杀。(你在考試之前(程序編譯期)已經(jīng)知道了試卷的信息)

第二場考試的試卷可能有50道甚至更多的題,并且老師沒有告訴你們?nèi)魏晤}目的信息拾因。(你只有在考試真正開始(程序運行期)才能知道試卷的信息)

得出的結(jié)論是什么旺罢?顯然相對于第一場考試旷余,完成第二場考試我們需要花費更多的時間。

為什么堆內(nèi)存的靈活性高于棧內(nèi)存扁达?

這就更好理解了正卧,一個要求數(shù)據(jù)的自身信息都必須被確定。一個可以動態(tài)的分配內(nèi)存大小跪解,也不必事先了解存儲數(shù)據(jù)的任何信息炉旷。

何為靈活性?也就是我們可以有更多的變數(shù)叉讥。那么對應的窘行,規(guī)則越多,限制則越強图仓,靈活性也就越弱罐盔。所以堆內(nèi)存的靈活性自然高于棧內(nèi)存。

除了上面的特點以外救崔,棧還有很重要的一個特點:棧內(nèi)存中存儲的數(shù)據(jù)可以實現(xiàn)數(shù)據(jù)共享惶看!

假設(shè)我們同時定義了兩個變量:? int a = 100; int b = 100;

這時候編譯器的工作過程是:首先會在棧中開辟一塊名為”a“的存儲空間,然后查看棧中是否存放著一個”100“的值六孵,發(fā)現(xiàn)在棧中沒有找到這樣的一個值纬黎,那么向棧中加入一個”100“的值,讓”a“等于這個值狸臣。繼而再在棧中開辟一塊名為”b“的存儲空間莹桅,這時候棧中已經(jīng)存在一個”100“的值昌执,那么就直接讓”b“也等于這個值就行了烛亦。

由此我們發(fā)現(xiàn),在完成對“a”的存儲分配后懂拾,再存儲“b”時煤禽,我們并沒有再次向柜子放進一個“100”,而是直接將前一次放進棧中的“100”的地址拿給“b”岖赋,棧里面”100“這個值同時功共享給了變量”a“和”b“檬果,這就是棧內(nèi)存中的數(shù)據(jù)共享。那么唐断,你可能會想选脊,實現(xiàn)數(shù)據(jù)共享的好處是什么?自然是節(jié)約內(nèi)存空間脸甘,既然同樣的值可以實現(xiàn)共享恳啥,那么就避免了反復像內(nèi)存中加入同樣的值。

那么丹诀,接下再看另一個例子(String類型的存儲是相對比較特殊的):

String s1 = "abc";

String s2 = "abc";

System.out.print(s1==s2);

這里的打印結(jié)果會是什么钝的?我們可能會這樣思考:

因為String是對象類型翁垂,定義了s1和s2兩個對象引用,分別指向值同樣為”abc“的兩個String類型對象硝桩。

Java中沿猜,”=="用于比較兩個對象引用時碗脊,實際是在比較這兩個引用是否指向同一個對象啼肩。

所以這里應該會打印false。但事實上衙伶,打印的結(jié)果為true疟游。這是由于什么原因造成的?

要搞清楚這個過程痕支,首先要理解:String s = "abc"和String s = new String("abc")兩張聲明方式的不同之處:

如果是使用String s = "abc"這種形式颁虐,也就是直接用雙引號定義的形式。

可以看做我們聲明了一個值為”abc“的字符串對象引用變量s卧须。

但是另绩,由于String類是final的,所以事實上花嘶,可以看做是聲明了一個字符串引用常量笋籽。存放在常量池中。

如果是使用關(guān)鍵字new這種形式聲明出的椭员,則是在程序運行期被動態(tài)創(chuàng)建车海,存放在堆中。

所以隘击,對于字符串而言侍芝,如果是編譯期已經(jīng)創(chuàng)建好(直接用雙引號定義的)的就存儲在常量池中;

如果是運行期(new出來的)才能確定的就存儲在堆中埋同。

對于equals相等的字符串州叠,在常量池中永遠只有一份,在堆中可以有多份凶赁。

了解了字符串存儲的這種特點咧栗,就可以對上面兩種不同的聲明方式進一步細化理解:

String s = ”abc“的工作過程可以分為以下幾個步驟:

(1)定義了一個名為"s"的String類型的引用。

(2)檢查在常量池中是否存在值為"abc"的字符串對象致板;

(3)如果不存在翰灾,則在常量池(字符串池)創(chuàng)建存儲進一個值為"abc"的字符串對象平斩。如果已經(jīng)存在,則跳過這一步工作晚凿。

(4)將對象引用s指向字符串池當中的”abc“對象。

String s = new String(”abc“)的步驟則為:

(1)定義了一個名為"s"的String類型的引用。

(2)檢查在常量池中是否存在值為"abc"的字符串對象;

(3)如果不存在,則在常量池(字符串池)存儲進一個值為"abc"的字符串對象。如果已經(jīng)存在,則跳過這一步工作蚁孔。

(4)在堆中創(chuàng)建存儲一個”abc“字符串對象。

(5)將對象引用指向堆中的對象摆尝。

這里指的注意的是,采用new的方式卫旱,雖然是在堆中存儲對象投放,但是也會在存儲之前檢查常量池中是否已經(jīng)含有此對象,如果沒有适贸,則會先在常量池創(chuàng)建對象跪呈,然后在堆中創(chuàng)建這個對象的”拷貝對象“。這也就是為什么有道面試題:String s = new String(“xyz”);產(chǎn)生幾個對象取逾?的答案是:一個或兩個的原因耗绿。因為如果常量池中原來沒有”xyz”,就是兩個。

弄清楚了原理砾隅,再看上面的例子误阻,就知道為什么了。在執(zhí)行String s1 = 'abc"時;常量池中還沒有對象晴埂,所以創(chuàng)建一個對象究反。之后在執(zhí)行String s2 = 'abc"的時候,因為常量池中已經(jīng)存在了"abc'對象儒洛,所以說s2只需要指向這個對象就完成工作了精耐。那么s1和s2指向同一個對象,用”==“比較自然返回true。所以常量池與棧內(nèi)存一樣琅锻,也可以實現(xiàn)數(shù)據(jù)共享卦停。

還有值得注意的一點的就是:我們知道局部變量存儲于棧內(nèi)存當中。那么成員變量呢恼蓬?答案是:成員變量的數(shù)據(jù)存儲于堆中該成員變量所屬的對象里面惊完。

而棧內(nèi)存與堆內(nèi)存的另一不同點在于,堆內(nèi)存中存放的變量都會進行默認初始化处硬,而棧內(nèi)存中存放的變量卻不會小槐。

這也就是為什么,我們在聲明一個成員變量時荷辕,可以不用對其進行初始化賦值凿跳。而如果聲明一個局部變量卻未進行初始賦值件豌,如果想對其進行使用就會報編譯異常的原因了。

最后控嗜,借助網(wǎng)上看到的一個例子幫助對棧內(nèi)存茧彤,堆內(nèi)存的存儲進行理解:

class BirthDate {

private int day;

private int month;

private int year;

public BirthDate(int d, int m, int y) {

day = d;

month = m;

year = y;

}

省略get,set方法………

}

public class Test{

public static void main(String args[]){

int date = 9;

Test test = new Test();

test.change(date);

BirthDate d1= new BirthDate(7,7,1970);

}

public void change1(int i){

i = 1234;

}

}

對于以上這段代碼,date為局部變量躬审,i,d,m,y都是形參為局部變量棘街,day,month承边,year為成員變量遭殉。下面分析一下代碼執(zhí)行時候的變化:

1. main方法開始執(zhí)行:int date = 9;

date局部變量,基礎(chǔ)類型博助,引用和值都存在棧中险污。

2. Test test = new Test();

test為對象引用,存在棧中富岳,對象(new Test())存在堆中蛔糯。

3. test.change(date);

調(diào)用change(int i)方法,i為局部變量窖式,引用和值存在棧中蚁飒。當方法change執(zhí)行完成后,i就會從棧中消失萝喘。

4. BirthDate d1= new BirthDate(7,7,1970);

調(diào)用BIrthDate類的構(gòu)造函數(shù)生成對象淮逻。

d1為對象引用,存在棧中阁簸;

對象(new BirthDate())存在堆中爬早;

其中d,m,y為局部變量存儲在棧中,且它們的類型為基礎(chǔ)類型启妹,因此它們的數(shù)據(jù)也存儲在棧中筛严;

day,month,year為BirthDate對象的的成員變量,它們存儲在堆中存儲的new BirthDate()對象里面饶米;

當BirthDate構(gòu)造方法執(zhí)行完之后桨啃,d,m,y將從棧中消失。

5.main方法執(zhí)行完之后咙崎。

date變量优幸,test,d1引用將從棧中消失褪猛;

new Test(),new BirthDate()將等待垃圾回收器進行回收。

轉(zhuǎn)自:http://www.xuebuyuan.com/2225286.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末羹饰,一起剝皮案震驚了整個濱河市伊滋,隨后出現(xiàn)的幾起案子碳却,更是在濱河造成了極大的恐慌,老刑警劉巖笑旺,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昼浦,死亡現(xiàn)場離奇詭異,居然都是意外死亡筒主,警方通過查閱死者的電腦和手機关噪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乌妙,“玉大人使兔,你說我怎么就攤上這事√僭希” “怎么了虐沥?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長泽艘。 經(jīng)常有香客問我欲险,道長,這世上最難降的妖魔是什么匹涮? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任天试,我火速辦了婚禮,結(jié)果婚禮上然低,老公的妹妹穿的比我還像新娘喜每。我一直安慰自己,他們只是感情好脚翘,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布灼卢。 她就那樣靜靜地躺著,像睡著了一般来农。 火紅的嫁衣襯著肌膚如雪鞋真。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天沃于,我揣著相機與錄音涩咖,去河邊找鬼。 笑死繁莹,一個胖子當著我的面吹牛檩互,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咨演,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼闸昨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起饵较,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤拍嵌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后循诉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體横辆,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年茄猫,在試婚紗的時候發(fā)現(xiàn)自己被綠了狈蚤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡划纽,死狀恐怖脆侮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阿浓,我是刑警寧澤他嚷,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站芭毙,受9級特大地震影響筋蓖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜退敦,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一粘咖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侈百,春花似錦瓮下、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至例证,卻和暖如春路呜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背织咧。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工胀葱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笙蒙。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓抵屿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捅位。 傳聞我的和親對象是個殘疾皇子轧葛,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 不知道大家有沒有這樣一種感覺朝群,程序員的數(shù)量井噴了燕耿≈蟹可能是因為互聯(lián)網(wǎng)火了姜胖,也可能是各家培訓機構(gòu)為我們拉來了大量...
    活這么大就沒飽過閱讀 2,723評論 6 26
  • 在函數(shù)中定義的一些基本類型的變量和對象的引用變量都在函數(shù)的棧內(nèi)存中分配。 當在一段代碼塊定義一個變量時淀散,Java就...
    木有魚丸啦閱讀 553評論 0 0
  • 文章摘錄地址:http://www.cnblogs.com/iliuyuet/p/5603618.html 1.棧...
    蘑菇姐夫閱讀 1,020評論 0 5
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法郭膛,內(nèi)部類的語法晨抡,繼承相關(guān)的語法,異常的語法则剃,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • 1. 五種存儲位置 1.1 寄存器 最快的存儲區(qū)耘柱,位于處理器中,數(shù)量及其有限棍现。所以寄存器根據(jù)需求進行分配调煎,不能人為...
    DanielHan閱讀 1,948評論 0 51