堆和棧的區別,爲什麼堆更慢?

棧與堆都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等 指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因爲它是在運行時 動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類 型的變量(,int, short, long, byte, float, double, boolean, char)和對象句柄。
棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:
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=4;那麼編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
要注意這種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因爲這種情況a的修改並不會影響到b, 它是由編譯器完成的,它有利於節省空間。而一個對象引用變量修改了這個對象的內部狀態,會影響到另一個對象引用變量。
String是一個特殊的包裝類數據。可以用:
String str = new String("abc");
String str = "abc";
兩種的形式來創建,第一種是用new()來新建對象的,它會在存放於堆中。每調用一次就會創建一個新的對象。
而第二種是先在棧中創建一個對String類的對象引用變量str,然後查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,並令str指向”abc”,如果已經有”abc” 則直接令str指向“abc”。
比較類裏面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一個對象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的對象。每一次生成一個。
因此用第二種方式創建多個”abc”字符串,在內存中其實只存在一個對象而已. 這種寫法有利與節省內存空間. 同時它可以在一定程度上提高程序的運行速度,因爲JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對於String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字符串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。
另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認爲,創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!而可能只是指向一個先前已經創建的 對象。只有通過new()方法才能保證每次都創建一個新的對象。
由於String類的immutable性質,當String變量需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。
====================================================================================================================
Java虛擬機提供了程序運行時環境,運行時環境中最重要的一個資源是運行時數據區。
運行時數據區是操作系統爲Java虛擬機進程分配的內存區域,並由虛擬機管轄,同時這塊區域又分爲若干個子區域,
主要包括堆區、方法區和Java棧區。
在堆區中存放對象,
在方法區存放類的類型信息,類型信息包括靜態變量和方法信息,方法信息中包含類的所有方法的字節碼。

棧,有個特點,先進去的後出來,裏邊放的是進程中正在執行的方法,
比如說方法


public void A(){

  B();
}
public void B(){
    C();
}
public void C(){

}
假設一個程序只調用A()方法,(開始時棧是空的):

程序運行的時候,先執行A()方法,因爲A方法調用了B()方法,這時因爲A()沒有執行完,中斷A()方法,將其放入棧中暫存,(此時因爲棧原來是空 的,A方法被push進棧後,位於棧底),執行B()方法,執行時因爲B方法調用了C()方法,此時中斷B方法的執行,將B()方法push入棧,由於棧 中已經有A方法,B被push進去後,位於A的上面;C()方法執行完後,去棧中查找位於棧頂的方法,查到是方法B,方法B繼續執行,執行完後再去棧中查 找位於棧頂的方法,此時棧內只又一個方法,那就是方法A,方法A有繼續執行,A執行完後,棧中已經沒有方法,程序運行結束。

假設下邊這個柱狀的東西是棧
-----------------------------------
|  |
|B()|---位於棧口最近的方法先被pop出來執行完成
|A()|---棧底的方法最後執行完
———

==================================================================================================================
java 的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray等指令建 立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因爲它是在運行時動態分 配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本 類型的變量(,int, short, long, byte, float, double, boolean, char)和對象句柄。

基本數據類型存儲在“棧”中,對象引用類型實際存儲在“堆”中,在棧中只是保留了引用內存的地址值。
=================================================================================================================
棧中的數據佔內存大小在編譯時是確定的,比如一個int類型就佔4B,所以變量地址好計算,所以分配和銷燬和訪問速度都比較快.
堆中的數據佔內存大小一般在編譯時是不確定的,在運行時才能知道大小,所以其地址只有在運行時計算,而且運行時可能佔內存大小還有變動,所以對這樣的數據的分配,銷燬和訪問都非常不方便,速度也慢一些.
===================================================================================================================
棧裏放的是地址,堆裏可以放數據也可以放地址(想象下堆裏的東西也有可能指向別的地方)
每個地址都會指向給定的數據,不然就沒有存在的必要了,同樣的道理,堆中的數據沒有被指針指向的話,也沒有存在的必要了,所以當obj=null時就釋放內存了。
Java有個好處就是沒有指針,Java中的傳遞的都是傳引用,不像c++還能傳地址,比如指針p++和p+1兩個的結果完全不同。
====================================================================================================================
棧是編譯時分配空間,而堆是動態分配(運行時分配空間),所以棧的速度快
cpu有專門的寄存器(esp,ebp)來操作棧,堆都是使用間接尋址的。棧快點。
發佈了56 篇原創文章 · 獲贊 8 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章