java中的內(nèi)存腐缤,分為兩種慢蜓,一為堆內(nèi)存,二為棧內(nèi)存。
棧內(nèi)存
在函數(shù)中定義的基本類型的變量和對象的引用變量都是在函數(shù)的棧內(nèi)存中分配闪檬。
當(dāng)在一段代碼塊中聲明了一個變量時星著,java就會在棧內(nèi)存中為這個變量分配內(nèi)存空間,當(dāng)超過變量的作用域之后粗悯,java也會自動釋放為該變量分配的空間虚循,而這個回收的空間可以即刻用作他用。
堆內(nèi)存
堆內(nèi)存用于存放由new創(chuàng)建的對象和數(shù)組样傍。
在堆內(nèi)存中分配的內(nèi)存空間横缔,由java虛擬機(jī)自動垃圾回收器來管理。在堆中產(chǎn)生了一個數(shù)組或者對象后衫哥,還可以在棧中定義一個特殊的變量剪廉,變量的值就等于數(shù)組或?qū)ο笤诙褍?nèi)存中的首地址,而這個棧中的特殊變量炕檩,也就成為數(shù)組或?qū)ο蟮囊米兞慷方R院罂梢栽诔绦蛑惺褂脳?nèi)存中的引用變量訪問堆內(nèi)存中的數(shù)組或?qū)ο罅恕R米兞肯喈?dāng)于是為數(shù)組或?qū)ο笃鸬囊粋€別名笛质,或者是代號泉沾。
數(shù)組和對象在沒有引用變量指向它的時候,才變成垃圾妇押,不能被繼續(xù)使用跷究,但是仍然會占用堆內(nèi)存空間,而后在一個不確定的時間內(nèi)敲霍,由java虛擬機(jī)自動垃圾回收器回收俊马,這也是java程序?yàn)槭裁磿加煤艽髢?nèi)存的原因。
一肩杈、 java中的內(nèi)存分配策略及堆和棧的比較
1. 內(nèi)存分配策略
根據(jù)編譯原理的觀點(diǎn)柴我,程序運(yùn)行時的內(nèi)存分配,有三種策略扩然,分別為靜態(tài)的艘儒、堆式的、棧式的夫偶。
靜態(tài)存儲分配指的是在編譯時就能確定每個數(shù)據(jù)目標(biāo)在運(yùn)行時的存儲空間需求界睁,因而在編譯時就給它們分配了固定的內(nèi)存空間。這種分配方式要求程序代碼中不能有可變數(shù)據(jù)結(jié)構(gòu)(例如可變數(shù)組)的存在兵拢,也不允許有嵌套或者遞歸的結(jié)構(gòu)出現(xiàn)翻斟,因?yàn)樗鼈兌紩?dǎo)致編譯時編譯程序無法準(zhǔn)確計算所需的存儲空間大小。
棧式存儲分配也可以成為動態(tài)存儲分配说铃,是由一個類似于堆棧的運(yùn)行棧來實(shí)現(xiàn)的访惜。和靜態(tài)存儲分配相反嘹履,在棧式分配方案中,程序?qū)τ诖鎯臻g的需要在編譯時是未知的疾牲,只有在運(yùn)行的時候才能知道植捎。但是規(guī)定在運(yùn)行進(jìn)入一個程序模塊時,必須要知道該程序模塊所需的數(shù)據(jù)區(qū)大小才能為其分配內(nèi)存阳柔。同我們?nèi)粘A私獾臈R恢卵媸啵瑮J酱鎯Ψ峙渥裾障冗M(jìn)后出的原則進(jìn)行分配。
堆式存儲分配專門負(fù)責(zé)在編譯時或運(yùn)行時程序模塊入口處都無法確定存儲要求的數(shù)據(jù)結(jié)構(gòu)的分配舌剂,比如可變長度串和對象實(shí)例济锄。堆內(nèi)存由大片的可利用塊或空閑塊組成,堆中的內(nèi)存也可以根據(jù)任意的順序進(jìn)行分配和釋放霍转。
堆和棧的比較
從通俗化的角度來說荐绝,堆是用來存放對象的,棧是用來存放執(zhí)行程序的
在編程中避消,例如在C/C++中低滩,所有的方法調(diào)用都是通過棧來進(jìn)行的,所有的局部變量岩喷、形式參數(shù)也都是通過棧來分配內(nèi)存的恕沫。使用的時候,根據(jù)棧的工作原理纱意,從棧頂向上用即可婶溯,Stack Pointer會自動指引程序到存儲位置,程序只需要進(jìn)行存儲即可偷霉。退出函數(shù)的時候迄委,修改棧指針即可將棧中存儲的內(nèi)容銷毀。這種模塊速度最快类少,適合用戶存儲執(zhí)行程序叙身。但是應(yīng)當(dāng)注意的是,在進(jìn)行內(nèi)存分配是瞒滴,比如為一個即將要調(diào)用的程序模塊分配數(shù)據(jù)區(qū)時曲梗,應(yīng)當(dāng)實(shí)現(xiàn)知道這個所需數(shù)據(jù)區(qū)的大小,也就是說雖然分配工作是在運(yùn)行程序的時候進(jìn)行的妓忍,但是分配的大小是在運(yùn)行程序之前就知道的,這個是編譯時確定的愧旦,而不是運(yùn)行時世剖。
堆是應(yīng)用程序在運(yùn)行過程中請求操作系統(tǒng)給分配的內(nèi)存,由于是操作系統(tǒng)管理的內(nèi)存分配笤虫,所以在分配和銷毀是都需要占用時間旁瘫,因此堆的工作效率比較低祖凫。但是堆的優(yōu)點(diǎn)在于,編譯器不需要知道從堆中分配了多少的內(nèi)存空間酬凳,也不需要知道存儲的數(shù)據(jù)要在堆中停留多長的時間惠况,這也就使得用堆來保存數(shù)據(jù)有著更大的靈活性。事實(shí)上宁仔,面向?qū)ο蟮亩鄳B(tài)性的實(shí)現(xiàn)稠屠,堆內(nèi)存的分配必不可少,因?yàn)槎鄳B(tài)對象所需的數(shù)據(jù)區(qū)大小只有在運(yùn)行時確定了對象以后才能知道翎苫。在java中权埠,創(chuàng)建對象只需要使用new關(guān)鍵字即可,執(zhí)行這些代碼時煎谍,就會在堆中自動進(jìn)行數(shù)據(jù)的保存攘蔽。也就是因?yàn)檫@種靈活分配存儲空間的特性,堆內(nèi)存分配的工作效率不高呐粘。
JVM中的堆和棧
堆和棧都是java用來在內(nèi)存中存放數(shù)據(jù)的地方满俗,與C++不同的是,java自動管理堆和棧作岖,程序員不能自行設(shè)置堆和棧唆垃。
java中的堆就是運(yùn)行時存儲數(shù)據(jù)的區(qū)域,類的實(shí)例對象可以通過new鳍咱、new Array等指令建立
從中分配空間降盹,這些空間不需要程序代碼來顯式釋放。堆是由jvm自動垃圾回收器負(fù)責(zé)的谤辜,堆的優(yōu)勢是可以動態(tài)的分配內(nèi)存大小蓄坏,生存周期也不用實(shí)現(xiàn)告訴編譯器,因?yàn)榭臻g是在運(yùn)行時動態(tài)進(jìn)行內(nèi)存分配的丑念。如果堆中的數(shù)據(jù)確認(rèn)為垃圾涡戳,則jvm的自動垃圾回收器匯自動回收相應(yīng)的空間。但是缺點(diǎn)是脯倚,由于實(shí)在運(yùn)行時動態(tài)進(jìn)行空間分配渔彰,存取速度較慢。
棧的優(yōu)勢是:存取的速度都比堆要快推正,僅次于寄存器恍涂。棧數(shù)據(jù)可以共享,但是缺點(diǎn)時植榕,椩俨祝空間中的數(shù)據(jù)大小和生存期必須是確定的,缺乏靈活性尊残。棧主要存放一些基本類型的變量int, short, long, byte, float, double, boolean, char
和對象句柄炒瘸。
棧有一個很重要的特殊性淤堵,就是存在棧中的數(shù)據(jù)可以共享。假設(shè)我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中創(chuàng)建一個變量為a的引用顷扩,然后查找棧中是否有3這個值拐邪,如果沒找到,就將3存放進(jìn)來隘截,然后將a指向3扎阶。接著處理int b = 3;在創(chuàng)建完b的引用變量后,因?yàn)樵跅V幸呀?jīng)有3這個值技俐,便將b直接指向3乘陪。這樣,就出現(xiàn)了a與b同時均指向3的情況雕擂。這時啡邑,如果再令a=4;那么編譯器會重新搜索棧中是否有4值,如果沒有井赌,則將4存放進(jìn)來谤逼,并令a指向4;如果已經(jīng)有了,則直接將a指向這個地址仇穗。因此a值的改變不會影響到b的值流部。要注意這種數(shù)據(jù)的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因?yàn)檫@種情況a的修改并不會影響到b, 它是由編譯器完成的纹坐,它有利于節(jié)省空間枝冀。而一個對象引用變量修改了這個對象的內(nèi)部狀態(tài),會影響到另一個對象引用變量耘子。
參考:http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html