天天看點

Java中堆記憶體和棧記憶體的差別

     java中記憶體分為堆記憶體和棧記憶體。堆和棧都是資料結構,堆是一個完全的樹結構,存儲數組或者對象;棧是一個先進後出的棧,存儲常量。

  在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧記憶體中配置設定。

  當在一段代碼塊定義一個變量時,Java就在棧中為這個變量配置設定記憶體空間,當超過變量的作用域後,Java會自動釋放掉為該變量所配置設定的記憶體空間,該記憶體空間可以立即被另作他用。

  堆記憶體用來存放由new建立的對象和數組。

  在堆中配置設定的記憶體,由Java虛拟機的自動垃圾回收器來管理。

  在堆中産生了一個數組或對象後,還可以在棧中定義一個特殊的變量,讓棧中這個變量的取值等于數組或對象在堆記憶體中的首位址,棧中的這個變量就成了數組或對象的引用變量。

  引用變量就相當于是為數組或對象起的一個名稱,以後就可以在程式中使用棧中的引用變量來通路堆中的數組或對象。

  具體的說:

  棧與堆都是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, 它是由編譯器完成的,它有利于節省空間。而一個對象引用變量修改了這個對象的内部狀态,會影響到另一個對象引用變量。

一:程式的記憶體配置設定

    棧區(stack)有編譯器自動配置設定釋放,存放函數的參數,局部變量等.

    堆區(heap)一般由程式員配置設定和釋放,否則就由OS回收。堆用于存放全局變量,靜态變量,常量字元串和函數代碼(函數體的二進制代碼).

二:申請後系統的響應

       棧:隻要棧的剩餘空間大于所申請的空間,系統将為程式提供記憶體,否則将報異常提示棧溢出。

       堆:在記錄空閑記憶體位址的連結清單中尋找一個空間大于所申請空間的堆結點,然後将該結點從空閑結點連結清單中删除,并将該結點的空間配置設定給程式。另外,對于大多數系統會在這塊記憶體空間的首位址出記錄本次配置設定空間的大小,這樣代碼中的delete 才能正确釋放本記憶體空間。系統會将多餘的那部分重新空閑連結清單中。

三:申請大小的限制

       棧:在windows中,棧是向低位址擴充的資料結構,是一塊連續的記憶體區域。也就是棧頂的位址和棧的最大容量(windows是2M)是系統規定好的。如果申請的空間超過剩餘棧空間,将提示overflow,是以從棧獲得的空間較小。

       堆:堆是向高位址擴充的資料結構,是不連續的記憶體區域。堆的大小受限于系統中有效的虛拟記憶體。由此可見堆的記憶體獲得比較靈活,空間較大。

四:棧和堆的存儲内容

       棧:在棧中,第一個進棧的是主函數下一條指令的位址,然後是函數的各個參數,在大多數編譯器中,參數是由右往左入棧,然後是函數中的局部變量。注意,靜态變量不入棧。出棧則剛好順序相反。

       堆:一般在堆的頭部用一個位元組存放堆的大小,具體内容由程式員安排。