『壹』 heap和stack有什麼區別
1.heap是堆,stack是棧。2.stack的空間由操作系統自動分配和釋放,heap的空間是手動申請和釋放的,heap常用new關鍵字來分配。3.stack空間有限,heap的空間是很大的自由區。在java中,若只是聲明一個對象,則先在棧內存中為其分配地址空間,若再new一下,實例化它,則在堆內存中為其分配地址。4.舉例:數據類型 變數名;這樣定義的東西在棧區。如:Object a =null; 只在棧內存中分配空間new 數據類型();或者malloc(長度); 這樣定義的東西就在堆區如:Object b =new Object(); 則在堆內存中分配空間
『貳』 heap和stack有什麼區別 java
stack棧是一種線形集合,其添加和刪除元素的操作應在同一段完成。stack棧按照後進先出的方式進行處理。
heap堆是棧的一個組成元素.
在JVM中,內存分為兩個部分,Stack(棧)和Heap(堆),這里,我們從JVM的內存管理原理的角度來認識Stack和Heap,並通過這些原理認清Java中靜態方法和靜態屬性的問題。
一般,JVM的內存分為兩部分:Stack和Heap。
Stack(棧)是JVM的內存指令區。Stack管理很簡單,push一定長度位元組的數據或者指令,Stack指針壓棧相應的位元組位移;pop一定位元組長度數據或者指令,Stack指針彈棧。Stack的速度很快,管理很簡單,並且每次操作的數據或者指令位元組長度是已知的。所以Java 基本數據類型,Java 指令代碼,常量都保存在Stack中。
Heap(堆)是JVM的內存數據區。Heap 的管理很復雜,每次分配不定長的內存空間,專門用來保存對象的實例。在Heap 中分配一定的內存來保存對象實例,實際上也只是保存對象實例的屬性值,屬性的類型和對象本身的類型標記等,並不保存對象的方法(方法是指令,保存在Stack中),在Heap 中分配一定的內存保存對象實例和對象的序列化比較類似。而對象實例在Heap 中分配好以後,需要在Stack中保存一個4位元組的Heap 內存地址,用來定位該對象實例在Heap 中的位置,便於找到該對象實例。
由於Stack的內存管理是順序分配的,而且定長,不存在內存回收問題;而Heap 則是隨機分配內存,不定長度,存在內存分配和回收的問題;因此在JVM中另有一個GC進程,定期掃描Heap ,它根據Stack中保存的4位元組對象地址掃描Heap ,定位Heap 中這些對象,進行一些優化(例如合並空閑內存塊什麼的),並且假設Heap 中沒有掃描到的區域都是空閑的,統統refresh(實際上是把Stack中丟失了對象地址的無用對象清除了),這就是垃圾收集的過程;關於垃圾收集的更深入講解請參考51CTO之前的文章《JVM內存模型及垃圾收集策略解析》。
JVM的體系結構
我們首先要搞清楚的是什麼是數據以及什麼是指令。然後要搞清楚對象的方法和對象的屬性分別保存在哪裡。
1)方法本身是指令的操作碼部分,保存在Stack中;
2)方法內部變數作為指令的操作數部分,跟在指令的操作碼之後,保存在Stack中(實際上是簡單類型保存在Stack中,對象類型在Stack中保存地址,在Heap 中保存值);上述的指令操作碼和指令操作數構成了完整的Java 指令。
3)對象實例包括其屬性值作為數據,保存在數據區Heap 中。
非靜態的對象屬性作為對象實例的一部分保存在Heap 中,而對象實例必須通過Stack中保存的地址指針才能訪問到。因此能否訪問到對象實例以及它的非靜態屬性值完全取決於能否獲得對象實例在Stack中的地址指針。
非靜態方法和靜態方法的區別:
非靜態方法有一個和靜態方法很重大的不同:非靜態方法有一個隱含的傳入參數,該參數是JVM給它的,和我們怎麼寫代碼無關,這個隱含的參數就是對象實例在Stack中的地址指針。因此非靜態方法(在Stack中的指令代碼)總是可以找到自己的專用數據(在Heap 中的對象屬性值)。當然非靜態方法也必須獲得該隱含參數,因此非靜態方法在調用前,必須先new一個對象實例,獲得Stack中的地址指針,否則JVM將無法將隱含參數傳給非靜態方法。
靜態方法無此隱含參數,因此也不需要new對象,只要class文件被ClassLoader load進入JVM的Stack,該靜態方法即可被調用。當然此時靜態方法是存取不到Heap 中的對象屬性的。
總結一下該過程:當一個class文件被ClassLoader load進入JVM後,方法指令保存在Stack中,此時Heap 區沒有數據。然後程序技術器開始執行指令,如果是靜態方法,直接依次執行指令代碼,當然此時指令代碼是不能訪問Heap 數據區的;如果是非靜態方法,由於隱含參數沒有值,會報錯。因此在非靜態方法執行前,要先new對象,在Heap 中分配數據,並把Stack中的地址指針交給非靜態方法,這樣程序技術器依次執行指令,而指令代碼此時能夠訪問到Heap 數據區了。
靜態屬性和動態屬性:
前面提到對象實例以及動態屬性都是保存在Heap 中的,而Heap 必須通過Stack中的地址指針才能夠被指令(類的方法)訪問到。因此可以推斷出:靜態屬性是保存在Stack中的,而不同於動態屬性保存在Heap 中。正因為都是在Stack中,而Stack中指令和數據都是定長的,因此很容易算出偏移量,也因此不管什麼指令(類的方法),都可以訪問到類的靜態屬性。也正因為靜態屬性被保存在Stack中,所以具有了全局屬性。
在JVM中,靜態屬性保存在Stack指令內存區,動態屬性保存在Heap數據內存區。
『叄』 JAVA中Stack和Heap的區別
相同點:stack和heap都是內存區域
不同點:stack的內存區域小,heap的內存區域大。stack用於存放變數,地址,heap用於存放對象,是真正的存放對象的地方。
『肆』 java虛擬機內存中,heap和stack有什麼區別
1.Java中對象都是分配在heap(堆)中。從heap中分配內存所消耗的時間遠遠大於從stack產生存儲空間所需的時間。
(1)每個應用程序運行時,都有屬於自己的一段內存空間,用於存放臨時變數、參數傳遞、函數調用時的PC值的保存。這叫stack。
(2)所有的應用可以從一個系統共用的空間中申請供自己使用的內存,這個共用的空間叫heap。
(3)stack中的對象或變數只要定義好就可使用了,應用程序結束時會自動釋放。
(4)而要使用heap中申請的變數或對象只能定義變數指針,並要求在運行過程中通過new來動態分配內存空間,而且必須顯示地free你申請過的內存,不過Java的垃圾回收機解決了這個問題,它會幫你釋放這部分內存。
(5)stack中變數的大小和個數會影響exe的文件大小,但速度快。堆中的變數大小與exe大小關系不大,但分配和釋放需要耗費的時間遠大於stack中分配內存所需的時間。
2.在Java語言里堆(heap)和棧(stack)里的區別
1).
棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
2).
棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。另外,棧數據可以共享,詳見第3點。堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
3). Java中的數據類型有兩種。
一種是基本類型(primitive types), 共有8種,即int, short, long,
byte, float, double, boolean, char(注意,並沒有string的基本類型)。這種類型的定義是通過諸如int a = 3;
long b = 255L;的形式來定義的,稱為自動變數。值得注意的是,自動變數存的是字面值,不是類的實例,即不是類的引用,這里並沒有類的存在。如int a =
3;
這里的a是一個指向int類型的引用,指向3這個字面值。這些字面值的數據,由於大小可知,生存期可知(這些字面值固定定義在某個程序塊裡面,程序塊退出後,欄位值就消失了),出於追求速度的原因,就存在於棧中。
另外,棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義
int a = 3;
int b = 3;
編譯器先處理int a =
3;首先它會在棧中創建一個變數為a的引用,然後查找有沒有字面值為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然後將a指向3的地址。接著處理int b =
3;在創建完b的引用變數後,由於在棧中已經有3這個字面值,便將b直接指向3的地址。這樣,就出現了a與b同時均指向3的情況。
特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一個對象,如果一個對象引用變數修改了這個對象的內部狀態,那麼另一個對象引用變數也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟著改變的情況。如上例,我們定義完a與
b的值後,再令a=4;那麼,b不會等於4,還是等於3。在編譯器內部,遇到a=4;時,它就會重新搜索棧中是否有4的字面值,如果沒有,重新開闢地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
另一種是包裝類數據,如Integer,
String,
Double等將相應的基本數據類型包裝起來的類。這些類數據全部存在於堆中,Java用new()語句來顯示地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要佔用更多的時間。
4).每個JVM的線程都有自己的私有的棧空間,隨線程創建而創建,java的stack存放的是frames
,java的stack和c的不同,只是存放本地變數,返回值和調用方法,不允許直接push和pop frames ,因為frames
可能是有heap分配的,所以java的stack分配的內存不需要是連續的。java的heap是所有線程共享的,堆存放所有 runtime data
,裡面是所有的對象實例和數組,heap是JVM啟動時創建。
5). String是一個特殊的包裝類數據。即可以用String str = new String("abc");的形式來創建,也可以用String
str = "abc";的形式來創建(作為對比,在JDK 5.0之前,你從未見過Integer i =
3;的表達式,因為類與字面值是不能通用的,除了String。而在JDK 5.0中,這種表達式是可以的!因為編譯器在後台進行Integer i = new
Integer(3)的轉換)。前者是規范的類的創建過程,即在Java中,一切都是對象,而對象是類的實例,全部通過new()的形式來創建。Java
中的有些類,如DateFormat類,可以通過該類的getInstance()方法來返回一個新創建的類,似乎違反了此原則。其實不然。該類運用了單例模式來返回類的實例,只不過這個實例是在該類內部通過new()來創建的,而getInstance()向外部隱藏了此細節。那為什麼在String
str = "abc";中,並沒有通過new()來創建實例,是不是違反了上述原則?其實沒有。
5.1). 關於String str =
"abc"的內部工作。Java內部將此語句轉化為以下幾個步驟:
(1)先定義一個名為str的對String類的對象引用變數:String
str;
(2)在棧中查找有沒有存放值為"abc"的地址,如果沒有,則開辟一個存放字面值為"abc"的地址,接著創建一個新的String類的對象o,並將o
的字元串值指向這個地址,而且在棧中這個地址旁邊記下這個引用的對象o。如果已經有了值為"abc"的地址,則查找對象o,並返回o的地址。
(3)將str指向對象o的地址。
值得注意的是,一般String類中字元串值都是直接存值的。但像String
str = "abc";這種場合下,其字元串值卻是保存了一個指向存在棧中數據的引用!
為了更好地說明這個問題,我們可以通過以下的幾個代碼進行驗證。
String str1 = "abc";
String str2 =
"abc";
System.out.println(str1==str2);
//true
注意,我們這里並不用str1.equals(str2);的方式,因為這將比較兩個字元串的值是否相等。==號,根據JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而我們在這里要看的是,str1與str2是否都指向了同一個對象。
結果說明,JVM創建了兩個引用str1和str2,但只創建了一個對象,而且兩個引用都指向了這個對象。
我們再來更進一步,將以上代碼改成:
String str1 = "abc";
String str2 = "abc";
str1 =
"bcd";
System.out.println(str1 + "," + str2); //bcd,
abc
System.out.println(str1==str2); //false
這就是說,賦值的變化導致了類對象引用的變化,str1指向了另外一個新對象!而str2仍舊指向原來的對象。上例中,當我們將str1的值改為"bcd"時,JVM發現在棧中沒有存放該值的地址,便開辟了這個地址,並創建了一個新的對象,其字元串的值指向這個地址。
事實上,String類被設計成為不可改變(immutable)的類。如果你要改變其值,可以,但JVM在運行時根據新值悄悄創建了一個新對象,然後將這個對象的地址返回給原來類的引用。這個創建過程雖說是完全自動進行的,但它畢竟佔用了更多的時間。在對時間要求比較敏感的環境中,會帶有一定的不良影響。
再修改原來代碼:
String str1 = "abc";
String str2 = "abc";
str1 =
"bcd";
String str3 =
str1;
System.out.println(str3); //bcd
String str4 = "bcd";
System.out.println(str1 == str4);
//true
str3
這個對象的引用直接指向str1所指向的對象(注意,str3並沒有創建新對象)。當str1改完其值後,再創建一個String的引用str4,並指向因str1修改值而創建的新的對象。可以發現,這回str4也沒有創建新的對象,從而再次實現棧中數據的共享。
我們再接著看以下的代碼。
String str1 = new String("abc");
String str2 =
"abc";
System.out.println(str1==str2); //false
創建了兩個引用。創建了兩個對象。兩個引用分別指向不同的兩個對象。
String str1 = "abc";
String str2 = new
String("abc");
System.out.println(str1==str2); //false
創建了兩個引用。創建了兩個對象。兩個引用分別指向不同的兩個對象。
以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創建,而且其字元串是單獨存值的,即使與棧中的數據相同,也不會與棧中的數據共享。
6). 數據類型包裝類的值不可修改。不僅僅是String類的值不可修改,所有的數據類型包裝類都不能更改其內部的值。
7). 結論與建議:
(1)我們在使用諸如String str =
"abc";的格式定義類時,總是想當然地認為,我們創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!唯一可以肯定的是,指向
String類的引用被創建了。至於這個引用到底是否指向了一個新的對象,必須根據上下文來考慮,除非你通過new()方法來顯要地創建一個新的對象。因此,更為准確的說法是,我們創建了一個指向String類的對象的引用變數str,這個對象引用變數指向了某個值為"abc"的String類。清醒地認識到這一點對排除程序中難以發現的bug是很有幫助的。
(2)使用String str =
"abc";的方式,可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對於String str = new
String("abc");的代碼,則一概在堆中創建新對象,而不管其字元串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。這個思想應該是享元模式的思想,但JDK的內部在這里實現是否應用了這個模式,不得而知。
(3)當比較包裝類裡面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==。
(4)由於String類的immutable性質,當String變數需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。
如果java不能成功分配heap的空間,將拋出OutOfMemoryError
『伍』 java中堆區和棧區的區別
java中堆(heap)和堆棧(stack)有什麼區別 stack 和 heep 都是內存的一部分
stack 空間小,速度比較快, 用來放對象的引用
heep 大,一般所有創建的對象都放在這里。
棧(stack):是一個先進後出的數據結構,通常用於保存方法(函數)中的參數,局部變數.
在java中,所有基本類型和引用類型都在棧中存儲.棧中數據的生存空間一般在當前scopes內(就是由{...}括起來的區域).
堆(heap):是一個可動態申請的內存空間(其記錄空閑內存空間的鏈表由操作系統維護),C中的malloc語句所產生的內存空間就在堆中.
在java中,所有使用new xxx()構造出來的對象都在堆中存儲,當垃圾回收器檢測到某對象未被引用,則自動銷毀該對象.所以,理論上說java中對象的生存空間是沒有限制的,只要有引用類型指向它,則它就可以在任意地方被使用.
1. 棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
2. 棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。另外,棧數據可以共享,詳見第3點。堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
『陸』 java棧stack和堆heap的工作原理,用途和區別
java堆和棧的區別
Java中內存分成兩種:一種是棧stack,一種是堆heap。
函數中的一些基本類型的變數(int, float)和對象的引用變數(reference)都在函數的棧中,馬克-to-win,(工作於編譯階段, 生成class文件之前)分配。存取速度快,稍遜於寄存器, 比堆快,
函數執行完後,Java會自動釋放掉為函數里變數開辟的棧內存空間,該內存空間可以立即被另作他用。
堆heap內存用來存放由new創建的對象和數組。堆內存,負責運行時(runtime, 執行生成的class文件時)數據,由JVM的自動管理。缺點是,存取速度較慢。
棧中的引用變數指向堆中的對象或數組。
棧
中有共享池的概念,比如下面例子中,sz="hello";在棧中創建一個String對象引用變數sz,然後看看棧中有沒有"hello",如果沒有,
則將"hello"存放進棧,並令sz指向」hello」,如果已經有」hello」 則直接令sz指向「hello」。對於int, float
類型的變數也是一樣的有這種共享池的概念,注意上述的工作是在compile(編譯)的階段完成的,而不是runtime運行時完成的。
對
於下面程序中:ss0 = new String( "hello"
);是用new()來新建對象的,存於堆中。每調用一次就會創建一個新的對象。當然從節省空間的角度來講,肯定不如str="hello",有童鞋一定
問,那要它有什麼用?當時設計編譯器時,為什麼要設計它?馬克-to-win,那我請問你,如果在你編程序時,你還不知道字元串內容怎麼辦?這時就用到
new String,所以,什麼都有什麼的用處。
public class Test
{
public static void main(String args[]) {
String str, str1, ss0, ss1, ss2, ss3, ss4;
str = "hello";
。。。。。。。。。。。。。。。。詳情網上找「馬克-to-win」,參考他的網站或他的網路空間:java第2章的內容