深入理解堆棧占调、堆在內(nèi)存中的實(shí)現(xiàn)

盡管在Java我們并不需要擔(dān)心內(nèi)存管理和垃圾回收(GarbageCollection)概漱,但是我們還是應(yīng)該了解它們丑慎,以優(yōu)化我們的應(yīng)用程序。同時(shí)犀概,還需要具備一些基礎(chǔ)的內(nèi)存管理工作機(jī)制的知識立哑,這樣能夠有助于解釋我們?nèi)粘3绦蚓帉懼械淖兞康男袨椤T诒疚闹形覍⒅v解棧和堆的基本知識姻灶,變量類型以及為什么一些變量能夠按照它們自己的方式工作铛绰。
當(dāng)我們的代碼執(zhí)行時(shí),內(nèi)存中有兩個(gè)地方用來存儲這些代碼产喉。假如你不曾了解捂掰,那就讓我來給你介紹棧(Stack)和堆(Heap)敢会。棧和堆都用來幫助我們運(yùn)行代碼的,它們駐留在機(jī)器內(nèi)存中这嚣,且包含所有代碼執(zhí)行所需要的信息鸥昏。
棧vs堆:有什么不同?
棧負(fù)責(zé)保存我們的代碼執(zhí)行(或調(diào)用)路徑,而堆則負(fù)責(zé)保存對象(或者說數(shù)據(jù)姐帚,接下來將談到很多關(guān)于堆的問題)的路徑吏垮。
可以將棧想象成一堆從頂向下堆疊的盒子。當(dāng)每調(diào)用一次方法時(shí)罐旗,我們將應(yīng)用程序中所要發(fā)生的事情記錄在棧頂?shù)囊粋€(gè)盒子中,而我們每次只能夠使用棧頂?shù)哪莻€(gè)盒子膳汪。當(dāng)我們棧頂?shù)暮凶颖皇褂猛曛螅蛘哒f方法執(zhí)行完畢之后九秀,我們將拋開這個(gè)盒子然后繼續(xù)使用棧頂上的新盒子遗嗽。堆的工作原理比較相似,但大多數(shù)時(shí)候堆用作保存信息而非保存執(zhí)行路徑鼓蜒,因此堆能夠在任意時(shí)間被訪問痹换。與棧相比堆沒有任何訪問限制,堆就像床上的舊衣服都弹,我們并沒有花時(shí)間去整理娇豫,那是因?yàn)榭梢噪S時(shí)找到一件我們需要的衣服,而棧就像儲物柜里堆疊的鞋盒缔杉,我們只能從最頂層的盒子開始取锤躁,直到發(fā)現(xiàn)那只合適的。


以上圖片并不是內(nèi)存中真實(shí)的表現(xiàn)形式或详,但能夠幫助我們區(qū)分棧和堆系羞。
棧是自行維護(hù)的,也就是說內(nèi)存自動維護(hù)棧霸琴,當(dāng)棧頂?shù)暮凶硬辉俦皇褂媒氛瘢鼘⒈粧伋觥O喾吹奈喑耍研枰紤]垃圾回收澎迎,垃圾回收用于保持堆的整潔性,沒有人愿意看到周圍都是贓衣服选调,那簡直太臭了夹供!

棧和堆里有些什么?
當(dāng)我們的代碼執(zhí)行的時(shí)候仁堪,棧和堆中主要放置了四種類型的數(shù)據(jù):值類型(Value Type)哮洽,引用類型(Reference Type),指針(Pointer)弦聂,指令(Instruction)鸟辅。
1.基本類型:
在java中氛什,所有被聲明為以下類型的事物被稱為基本類型:
byte short int long float double char boolean
2.引用類型:
所有的被聲明為以下類型的事物被稱為引用類型:
class interface delegate object string
3.指針:
在內(nèi)存管理方案中放置的第三種類型是類型引用,引用通常就是一個(gè)指針匪凉。指針(或引用)是不同于引用類型的枪眉,是因?yàn)楫?dāng)我們說某個(gè)事物是一個(gè)引用類型時(shí)就意味著我們是通過指針來訪問它的。指針是一塊內(nèi)存空間再层,而它指向另一個(gè)內(nèi)存空間贸铜。就像棧和堆一樣,指針也同樣要占用內(nèi)存空間树绩,但它的值是一個(gè)內(nèi)存地址或者為空萨脑。


4.指令:
在后面的文章中你會看到指令是如何工作的...

如何決定放哪兒?
這里有一條黃金規(guī)則:

  1. 引用類型總是放在堆中隐轩。
  2. 值類型和指針總是放在它們被聲明的地方饺饭。(這條稍微復(fù)雜點(diǎn),需要知道棧是如何工作的职车,然后才能斷定是在哪兒被聲明的瘫俊。)
    就像我們先前提到的,棧是負(fù)責(zé)保存我們的代碼執(zhí)行(或調(diào)用)時(shí)的路徑悴灵。當(dāng)我們的代碼開始調(diào)用一個(gè)方法時(shí),將放置一段編碼指令(在方法中)到棧上,緊接著放置方法的參數(shù)扛芽,然后代碼執(zhí)行到方法中的被“壓棧”至棧頂?shù)淖兞课恢没鳌Mㄟ^以下例子很容易理解...
    下面是一個(gè)方法(Method):
    public int AddFive(int pValue) { int result; result = pValue + 5; return result; }
    現(xiàn)在就來看看在棧頂發(fā)生了些什么川尖,記住我們所觀察的棧頂下實(shí)際已經(jīng)壓入了許多別的內(nèi)容。
    首先方法(只包含需要執(zhí)行的邏輯字節(jié)茫孔,即執(zhí)行該方法的指令叮喳,而非方法體內(nèi)的數(shù)據(jù))入棧,緊接著是方法的參數(shù)入棧缰贝。(我們將在后面討論更多的參數(shù)傳遞)



    接著馍悟,控制(即執(zhí)行方法的線程)被傳遞到堆棧中AddFive()的指令上,



    當(dāng)方法執(zhí)行時(shí)剩晴,我們需要在棧上為“result”變量分配一些內(nèi)存锣咒,

    Themethod finishes execution and our result is returned.方法執(zhí)行完成,然后方法的結(jié)果被返回赞弥。

    通過將棧指針指向AddFive()方法曾使用的可用的內(nèi)存地址毅整,所有在棧上的該方法所使用內(nèi)存都被清空,且程序?qū)⒆詣踊氐綏I献畛醯姆椒ㄕ{(diào)用的位置(在本例中不會看到)绽左。

    在這個(gè)例子中悼嫉,我們的"result"變量是被放置在棧上的,事實(shí)上妇菱,當(dāng)值類型數(shù)據(jù)在方法體中被聲明時(shí)承粤,它們都是被放置在棧上的暴区。

    值類型數(shù)據(jù)有時(shí)也被放置在堆上。記住這條規(guī)則--值類型總是放在它們被聲明的地方辛臊。好的仙粱,如果一個(gè)值類型數(shù)據(jù)在方法體外被聲明,且存在于一個(gè)引用類型中彻舰,那么它將被堆中的引用類型所取代伐割。
    來看另一個(gè)例子:
    假如我們有這樣一個(gè)MyInt類(它是引用類型因?yàn)樗且粋€(gè)類類型):
    public class MyInt { publicint MyValue; }
    然后執(zhí)行下面的方法:
    public MyInt AddFive(int pValue) { MyInt result = new MyInt(); result.MyValue = pValue + 5; return result; }
    就像前面提到的,方法及方法的參數(shù)被放置到棧上刃唤,接下來隔心,控制被傳遞到堆棧中AddFive()的指令上。



    接著會出現(xiàn)一些有趣的現(xiàn)象...
    因?yàn)?MyInt"是一個(gè)引用類型,它將被放置在堆上,同時(shí)在棧上生成一個(gè)指向這個(gè)堆的指針引用尚胞。
    在AddFive()方法被執(zhí)行之后硬霍,我們將清空...

    我們將剩下孤獨(dú)的MyInt對象在堆中(棧中將不會存在任何指向MyInt對象的指針!)

    這就是垃圾回收器(后簡稱GC)起作用的地方。當(dāng)我們的程序達(dá)到了一個(gè)特定的內(nèi)存閥值笼裳,我們需要更多的堆空間的時(shí)候唯卖,GC開始起作用。GC將停止所有正在運(yùn)行的線程躬柬,找出在堆中存在的所有不再被主程序訪問的對象拜轨,并刪除它們。然后GC會重新組織堆中所有剩下的對象來節(jié)省空間允青,并調(diào)整棧和堆中所有與這些對象相關(guān)的指針橄碾。你肯定會想到這個(gè)過程非常耗費(fèi)性能,所以這時(shí)你就會知道為什么我們需要如此重視棧和堆里有些什么颠锉,特別是在需要編寫高性能的代碼時(shí)法牲。
    Ok...這太棒了, 當(dāng)它是如何影響我的?
    Goodquestion.
    當(dāng)我們使用引用類型時(shí)木柬,我們實(shí)際是在處理該類型的指針皆串,而非該類型本身。當(dāng)我們使用值類型時(shí)眉枕,我們是在使用值類型本身恶复。聽起來很迷糊吧?
    同樣速挑,例子是最好的描述谤牡。
    假如我們執(zhí)行以下的方法:
    public int ReturnValue() { int x = new int(); x = 3; int y = new int(); y = x; y = 4; return x; }
    我們將得到值3,很簡單姥宝,對吧翅萤?
    假如我們首先使用MyInt類
    public class MyInt { public int MyValue; }
    接著執(zhí)行以下的方法:

    public int ReturnValue2() { MyInt x = new MyInt(); x.MyValue = 3; MyInt y = new MyInt(); y =x; y.MyValue =4; return x.MyValue; }
    我們將得到什么?... 4!
    為什么腊满?... x.MyValue怎么會變成4了呢套么?... 看看我們所做的然后就知道是怎么回事了:
    在第一例子中培己,一切都像計(jì)劃的那樣進(jìn)行著:
    public int ReturnValue() { int x = 3; int y = x; y = 4; return x; }


在第二個(gè)例子中,我們沒有得到"3"是因?yàn)樽兞?x"和"y"都同時(shí)指向了堆中相同的對象胚泌。

    public intReturnValue2()  {MyInt x;              x.MyValue = 3;       MyInt y;        y =x;           y.MyValue = 4;      return x.MyValue;     }

希望以上內(nèi)容能夠使你對java中的基本類型和引用類型的基本區(qū)別有一個(gè)更好的認(rèn)識省咨,并且對指針及指針是何時(shí)被使用的有一定的基本了解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玷室,一起剝皮案震驚了整個(gè)濱河市零蓉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌穷缤,老刑警劉巖敌蜂,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異津肛,居然都是意外死亡章喉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門快耿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囊陡,“玉大人,你說我怎么就攤上這事掀亥。” “怎么了妥色?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵搪花,是天一觀的道長。 經(jīng)常有香客問我嘹害,道長撮竿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任笔呀,我火速辦了婚禮幢踏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘许师。我一直安慰自己房蝉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布微渠。 她就那樣靜靜地躺著搭幻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逞盆。 梳的紋絲不亂的頭發(fā)上檀蹋,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音云芦,去河邊找鬼俯逾。 笑死贸桶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的桌肴。 我是一名探鬼主播刨啸,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼识脆!你這毒婦竟也來了设联?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤灼捂,失蹤者是張志新(化名)和其女友劉穎离例,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悉稠,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宫蛆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了的猛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耀盗。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卦尊,靈堂內(nèi)的尸體忽然破棺而出叛拷,到底是詐尸還是另有隱情,我是刑警寧澤岂却,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布忿薇,位于F島的核電站,受9級特大地震影響躏哩,放射性物質(zhì)發(fā)生泄漏署浩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一扫尺、第九天 我趴在偏房一處隱蔽的房頂上張望筋栋。 院中可真熱鬧,春花似錦正驻、人聲如沸弊攘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肴颊。三九已至,卻和暖如春渣磷,著一層夾襖步出監(jiān)牢的瞬間婿着,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竟宋,地道東北人提完。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像丘侠,于是被迫代替她去往敵國和親徒欣。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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