Java中Heap與Stack的區別

1) Heap是 Stack的一個子集.------擴展—>從內存觀點考慮。優化

2) Stack存取速度僅次於寄存器,存儲效率比heap高,可共享存儲數據,可是其中數據的大小和生存期必須在運行前肯定spa

3) Heap是運行時可動態分配的數據區,從速度看比Stack慢,Heap裏面的數據不共享,大小和生存期均可以在運行時再肯定。指針

4) new關鍵字 是運行時在Heap裏面建立對象,每new一次都必定會建立新對象,由於堆數據不共享。orm

Eg: String str1= new String("abc");   (1)對象

         String str2= "abc";               (2)進程

 str1是在Heap裏面建立的對象。內存

 str2是指向Stack裏面值爲「abc」的引用變量,語句(2)的執行,首先會建立引用變量str2, 再查找Stack裏面有沒有「abc」,有則將 str2指向 「abc」,沒有則在Stack裏面建立一個「abc」,再將str2指向「abc」。內存管理

由此可終結爲在創建一個對象時從兩個地方分配內存,在堆中分配的內存實際創建這個對象,而在棧中分配的內存只是一個指向這個堆對象的指針(引用)而已.io

heap和stack在內存中有什麼區別form

棧是一種線形集合,其添加和刪除元素的操做應在同一端尾部完成。棧按照後進先出的方式進行處理。堆是棧的一個組成元素.
堆存儲:heapstorage
堆存儲分配: heapstorage allocation
堆存儲管理: heap storage management
棧編址: stack addressing 
棧變換:stack transformation 
棧存儲器:stack memory
棧單元: stack cel

在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數據內存區。

相關文章
相關標籤/搜索