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ū)ο罅?。引用變量相?dāng)于是為數(shù)組或?qū)ο笃鸬囊粋€別名,或者是代號。
數(shù)組和對象在沒有引用變量指向它的時候,才變成垃圾,不能被繼續(xù)使用,但是仍然會占用堆內(nèi)存空間,而后在一個不確定的時間內(nèi),由java虛擬機(jī)自動垃圾回收器回收,這也是java程序為什么會占用很大內(nèi)存的原因。
一、 java中的內(nèi)存分配策略及堆和棧的比較
1. 內(nèi)存分配策略
根據(jù)編譯原理的觀點,程序運(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),因為它們都會導(dǎo)致編譯時編譯程序無法準(zhǔn)確計算所需的存儲空間大小。
棧式存儲分配也可以成為動態(tài)存儲分配,是由一個類似于堆棧的運(yùn)行棧來實現(xiàn)的。和靜態(tài)存儲分配相反,在棧式分配方案中,程序?qū)τ诖鎯臻g的需要在編譯時是未知的,只有在運(yùn)行的時候才能知道。但是規(guī)定在運(yùn)行進(jìn)入一個程序模塊時,必須要知道該程序模塊所需的數(shù)據(jù)區(qū)大小才能為其分配內(nèi)存。同我們?nèi)粘A私獾臈R恢?,棧式存儲分配遵照先進(jìn)后出的原則進(jìn)行分配。
堆式存儲分配專門負(fù)責(zé)在編譯時或運(yùn)行時程序模塊入口處都無法確定存儲要求的數(shù)據(jù)結(jié)構(gòu)的分配,比如可變長度串和對象實例。堆內(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)實現(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)點在于,編譯器不需要知道從堆中分配了多少的內(nèi)存空間,也不需要知道存儲的數(shù)據(jù)要在堆中停留多長的時間,這也就使得用堆來保存數(shù)據(jù)有著更大的靈活性。事實上,面向?qū)ο蟮亩鄳B(tài)性的實現(xiàn),堆內(nèi)存的分配必不可少,因為多態(tài)對象所需的數(shù)據(jù)區(qū)大小只有在運(yùn)行時確定了對象以后才能知道。在java中,創(chuàng)建對象只需要使用new關(guān)鍵字即可,執(zhí)行這些代碼時,就會在堆中自動進(jìn)行數(shù)據(jù)的保存。也就是因為這種靈活分配存儲空間的特性,堆內(nèi)存分配的工作效率不高。
JVM中的堆和棧
堆和棧都是java用來在內(nèi)存中存放數(shù)據(jù)的地方,與C++不同的是,java自動管理堆和棧,程序員不能自行設(shè)置堆和棧。
java中的堆就是運(yùn)行時存儲數(shù)據(jù)的區(qū)域,類的實例對象可以通過new、new Array等指令建立從中分配空間,這些空間不需要程序代碼來顯式釋放。堆是由jvm自動垃圾回收器負(fù)責(zé)的,堆的優(yōu)勢是可以動態(tài)的分配內(nèi)存大小,生存周期也不用實現(xiàn)告訴編譯器,因為空間是在運(yùn)行時動態(tài)進(jìn)行內(nèi)存分配的。如果堆中的數(shù)據(jù)確認(rèn)為垃圾,則jvm的自動垃圾回收器匯自動回收相應(yīng)的空間。但是缺點是,由于實在運(yùn)行時動態(tài)進(jìn)行空間分配,存取速度較慢。
棧的優(yōu)勢是:存取的速度都比堆要快,僅次于寄存器。棧數(shù)據(jù)可以共享,但是缺點時,棧空間中的數(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的引用變量后,因為在棧中已經(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ù)的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改并不會影響到b, 它是由編譯器完成的,它有利于節(jié)省空間。而一個對象引用變量修改了這個對象的內(nèi)部狀態(tài),會影響到另一個對象引用變量。
參考:http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html