引言:Java 中的堆和棧 Java把內(nèi)存劃分成兩種:一種是棧內(nèi)存,一種是堆內(nèi)存。至于“方法區(qū)”(靜態(tài)存儲(chǔ)區(qū)),可以理解為:主要存放靜態(tài)數(shù)據(jù)、全局 static 數(shù)據(jù)和常量。這塊內(nèi)存在程序編譯時(shí)就已經(jīng)分配好,并且在程序整個(gè)運(yùn)行期間都存在??偟膩?lái)說(shuō):堆和棧針對(duì)非靜態(tài)數(shù)據(jù),而方法區(qū)針對(duì)靜態(tài)數(shù)據(jù)。 一、堆內(nèi)存和棧內(nèi)存棧(stack)與堆(heap)都是Java用來(lái)在Ram中存放數(shù)據(jù)的地方。與C++不同,Java自動(dòng)管理?xiàng):投眩绦騿T不能直接地設(shè)置?;蚨?。 ·棧: o簡(jiǎn)單理解:堆棧(stack)是操作系統(tǒng)在建立某個(gè)進(jìn)程或者線程(在支持多線程的操作系統(tǒng)中是線程)為這個(gè)線程建立的存儲(chǔ)區(qū)域,該區(qū)域具有先進(jìn)后出的特性。 o特點(diǎn):存取速度比堆要快,僅次于直接位于CPU中的寄存器。棧中的數(shù)據(jù)可以共享(意思是:棧中的數(shù)據(jù)可以被多個(gè)變量共同引用)。 o缺點(diǎn):存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。 o相關(guān)存放對(duì)象:①一些基本類型的變量(,int, short, long, byte, float, double, boolean, char)和對(duì)象句柄【例如:在函數(shù)中定義的一些基本類型的變量和對(duì)象的引用變量】。②方法的形參 直接在??臻g分配,當(dāng)方法調(diào)用完成后從??臻g回收。 o特殊:①方法的引用參數(shù),在棧空間分配一個(gè)地址空間,并指向堆空間的對(duì)象區(qū),當(dāng)方法調(diào)用完成后從??臻g回收。②局部變量new出來(lái)之后,在??丶投芽臻g中分配空間,當(dāng)局部變量生命周期結(jié)束后,它的??臻g立刻被回收,它的堆空間等待GC回收。 ·堆: o簡(jiǎn)單理解:每個(gè)Java應(yīng)用都唯一對(duì)應(yīng)一個(gè)JVM實(shí)例,每一個(gè)JVM實(shí)例唯一對(duì)應(yīng)一個(gè)堆。應(yīng)用程序在運(yùn)行中所創(chuàng)建的所有類實(shí)例或者數(shù)組都放在這個(gè)堆中,并由應(yīng)用所有的線程共享。Java中分配堆內(nèi)存是自動(dòng)初始化的,Java中所有對(duì)象的存儲(chǔ)控件都是在堆中分配的,但這些對(duì)象的引用則是在棧中分配,也就是一般在建立一個(gè)對(duì)象時(shí),堆和棧都會(huì)分配內(nèi)存。 o特點(diǎn):可以動(dòng)態(tài)地分配內(nèi)存大小、比較靈活,生存期也不必事先告訴編譯器,Java的垃圾收集器會(huì)自動(dòng)收走這些不再使用的數(shù)據(jù)。在堆中分配的內(nèi)存,由Java虛擬機(jī)的自動(dòng)垃圾回收器來(lái)管理。 o缺點(diǎn):由于要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢。 o主要存放:①由new創(chuàng)建的對(duì)象和數(shù)組 ;②this o特殊:引用數(shù)據(jù)類型(需要用new來(lái)創(chuàng)建),既在??丶峙湟粋€(gè)地址空間,又在堆空間分配對(duì)象的類變量。 補(bǔ)充:在堆中產(chǎn)生了一個(gè)數(shù)組或?qū)ο蠛?,還可以在棧中定義一個(gè)特殊的變量,讓棧中這個(gè)變量的取值等于數(shù)組或?qū)ο笤诙褍?nèi)存中的首地址,棧中的這個(gè)變量就成了數(shù)組或?qū)ο蟮囊米兞俊?/p> 引用變量就相當(dāng)于是為數(shù)組或?qū)ο笃鸬囊粋€(gè)名稱,以后就可以在程序中使用棧中的引用變量來(lái)訪問(wèn)堆中的數(shù)組或?qū)ο蟆?/p> 引用變量是普通變量,定義時(shí)在棧中分配內(nèi)存,引用變量在程序運(yùn)行到作用域外釋放。而數(shù)組&對(duì)象本身在堆中分配,即使程序運(yùn)行到使用new產(chǎn)生數(shù)組和對(duì)象的語(yǔ)句所在地代碼塊之外,數(shù)組和對(duì)象本身占用的堆內(nèi)存也不會(huì)被釋放,數(shù)組和對(duì)象在沒(méi)有引用變量指向它的時(shí)候,才變成垃圾,不能再被使用,但是仍然占著內(nèi)存,在隨后的一個(gè)不確定的時(shí)間被垃圾回收器釋放掉。這個(gè)也是java比較占內(nèi)存的主要原因。 這里可以理解為: String s1 = new String('abc'); 這里面:'abc'表示棧中的一個(gè)存儲(chǔ)空間中的一個(gè)數(shù)據(jù),new String('abc')表示存在于堆中的一個(gè)對(duì)象,這個(gè)對(duì)象的值為‘a(chǎn)bc’,String s1則表示棧中定義的一個(gè)取了new String('abc')在堆中的首地址的一個(gè)特殊變量,也就是:s1成了引用變量,相當(dāng)于一個(gè)別名。 二、Java數(shù)據(jù)存儲(chǔ)和JVM內(nèi)存分區(qū)·在JAVA中,有六個(gè)不同的地方可以存儲(chǔ)數(shù)據(jù): 1.寄存器(register)。這是最快的存儲(chǔ)區(qū),因?yàn)樗挥诓煌谄渌鎯?chǔ)區(qū)的地方——處理器內(nèi)部。但是寄存器的數(shù)量極其有限,所以寄存器由編譯器根據(jù)需求進(jìn)行分配。你不能直接控制,也不能在程序中感覺(jué)到寄存器存在的任何跡象。 2.堆棧(stack)。位于通用RAM中,但通過(guò)它的“堆棧指針”可以從處理器哪里獲得支持。堆棧指針若向下移動(dòng),則分配新的內(nèi)存;若向上移動(dòng),則釋放那些內(nèi)存。這是一種快速有效的分配存儲(chǔ)方法,僅次于寄存器。創(chuàng)建程序時(shí)候,JAVA編譯器必須知道存儲(chǔ)在堆棧內(nèi)所有數(shù)據(jù)的確切大小和生命周期,因?yàn)樗仨毶上鄳?yīng)的代碼,以便上下移動(dòng)堆棧指針。這一約束限制了程序的靈活性,所以雖然某些Java數(shù)據(jù)存儲(chǔ)在堆棧中——特別是對(duì)象引用,但是JAVA對(duì)象不存儲(chǔ)其中。 3.堆(heap)。一種通用性的內(nèi)存池(也存在于RAM中),用于存放所以的JAVA對(duì)象。堆不同于堆棧的好處是:編譯器不需要知道要從堆里分配多少存儲(chǔ)區(qū)域,也不必知道存儲(chǔ)的數(shù)據(jù)在堆里存活多長(zhǎng)時(shí)間。因此,在堆里分配存儲(chǔ)有很大的靈活性。當(dāng)你需要?jiǎng)?chuàng)建一個(gè)對(duì)象的時(shí)候 ,只需要new寫一行簡(jiǎn)單的代碼,當(dāng)執(zhí)行這行代碼時(shí),會(huì)自動(dòng)在堆里進(jìn)行存儲(chǔ)分配。當(dāng)然,為這種靈活性必須要付出相應(yīng)的代碼。用堆進(jìn)行存儲(chǔ)分配比用堆棧進(jìn)行存儲(chǔ)存儲(chǔ)需要更多的時(shí)間。 4.靜態(tài)存儲(chǔ)(static storage)。這里的“靜態(tài)”是指“在固定的位置”。靜態(tài)存儲(chǔ)里存放程序運(yùn)行時(shí)一直存在的數(shù)據(jù)。你可用關(guān)鍵字static來(lái)標(biāo)識(shí)一個(gè)對(duì)象的特定元素是靜態(tài)的,但JAVA對(duì)象本身從來(lái)不會(huì)存放在靜態(tài)存儲(chǔ)空間里。 5.常量存儲(chǔ)(constant storage)。常量值通常直接存放在程序代碼內(nèi)部,這樣做是安全的,因?yàn)樗鼈冇肋h(yuǎn)不會(huì)被改變。有時(shí),在嵌入式系統(tǒng)中,常量本身會(huì)和其他部分分割離開(kāi),所以在這種情況下,可以選擇將其放在ROM中 。 6.非RAM存儲(chǔ)。如果數(shù)據(jù)完全存活于程序之外,那么它可以不受程序的任何控制,在程序沒(méi)有運(yùn)行時(shí)也可以存在。 就速度來(lái)說(shuō),有如下關(guān)系: 寄存器 < 堆棧="">< 堆=""><> ·JVM的內(nèi)存分區(qū): JVM的分區(qū)可分為三個(gè):堆(heap)、棧(stack)和方法區(qū)(method) 1.堆區(qū): o存儲(chǔ)的全是對(duì)象,每個(gè)對(duì)象都包含一個(gè)與之對(duì)應(yīng)的class信息(我們常說(shuō)的類類型,Clazz.getClass()等方式獲?。?,class目的是得到操作指令。 oJVM只有一個(gè)堆區(qū)(heap)被所有線程共享,堆中不存放基本類型和對(duì)象引用,只存放對(duì)象本身?!具@里的‘對(duì)象’,就不包括基本數(shù)據(jù)類型】 4.棧區(qū): o每個(gè)線程包含自己的一個(gè)棧區(qū),棧中只保存基本數(shù)據(jù)類型的對(duì)象和自定義對(duì)象的引用。 o每個(gè)棧中的數(shù)據(jù)(基本類型和對(duì)象引用)都是私有的,其他棧不可訪問(wèn)。 o棧 = 基本類型變量區(qū) + 執(zhí)行環(huán)境上下文 + 操作指令區(qū)(存放操作指令) 8.方法區(qū)【這個(gè)可能比較陌生】 o又稱為‘靜態(tài)區(qū)’,和堆一樣,被所有的線程共享。 o方法區(qū)包含所有的class和static變量。 補(bǔ)充:大家也許聽(tīng)說(shuō)過(guò)“數(shù)據(jù)區(qū)”或者“運(yùn)行時(shí)數(shù)據(jù)區(qū)”這個(gè)名詞,這里,我們說(shuō)JVM是驅(qū)動(dòng)Java程序運(yùn)行的基礎(chǔ),而它有三個(gè)分區(qū):堆、棧、方法區(qū),實(shí)際上,JVM的三個(gè)方法區(qū)就是包含于 JVM的運(yùn)行時(shí)數(shù)據(jù)區(qū)中的三大塊。于是,“數(shù)據(jù)區(qū)”與上述的分區(qū)的關(guān)系就明朗了。 三、Java的兩種數(shù)據(jù)類型:1.基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意, 并沒(méi)有string的基本類型)。這種類型的定義是通過(guò)諸如int a = 3; long b = 255L;的形式來(lái)定義的,稱為自動(dòng)變量。【自動(dòng)變量存的是字面值,不是類的實(shí)例(即不是類的引用),這里并沒(méi)有類的存在,如int a=3; 2.包裝類,如Integer, String, Double等將相應(yīng)的基本數(shù)據(jù)類型包裝起來(lái)的類。這些類數(shù)據(jù)全部存在于堆中,Java用new()語(yǔ)句來(lái)顯式地告訴編譯器,在運(yùn)行時(shí)才根據(jù)需要?jiǎng)討B(tài)創(chuàng)建,因此比較靈活,但缺點(diǎn)是要占用更多的時(shí)間。 視頻資料鏈接: data:text/html;charset=UTF-8;base64, 5oGt5Zac5L2g77yM5p625p6E5biI5a2m5Lmg576k5Y+35pivNTc1NzUxODU0Cg== 復(fù)制粘貼在網(wǎng)站打開(kāi)即可 |
|
來(lái)自: Frank_Chia > 《JAVA》