jvm 棧和堆

  若想使自己編寫的Java程序高效運行,以及進行正確、高效的異常診斷,JVM是不得不談的一個話題。本”JVM進階“專欄大部分內容均來源於經典書籍《深入理解Java虛擬機》。
  

  下面言歸正傳,本文重點從虛擬機內存模型(運行時數據區域)入手。先看圖:
java虛擬機內存模型

  這是一張比較官方的虛擬機模型圖,今天講的就是虛線框中棧的部分。
  棧是我們最常用的內存區域。它主要用來存放基本類型變量,局部變量以及對象的引用。例如:User user = new User();這裏的user就是對象的引用也可以理解爲地址,指引着虛擬機要去哪裏找user這個對象。 他們的基本關係如圖:

java內存及引用

  由圖可知,當我們將一個對象作爲方法的參數時,我們在方法中改變對象的值,也會影響到原來的對象的值,因爲我們只是改變了圖中內存區域的值,他的指引(地址)還是一樣的。同時也可以看出,棧的內存區域是連續的,有大小限制的,如果超過了就會拋出棧溢出的異常StackOverflowError。

  在每個方法執行的時候,都會創建一個個的棧幀,用於保存局部變量表,操作數棧,動態鏈接等信息(以後都會詳細講解)。每次方法的調用都會對應着一個棧幀,因此可以解釋有我們在寫遞歸程序的時候會不小心報棧溢出的異常,因爲棧是有限的,方法調用太多次導致棧幀堆滿了棧,所以溢出。看下面代碼:

  在參數-Xss128k的情況下的報錯。(eclipse中設置參數:右鍵代碼選擇Run As–>Run Configurations,在Arguments欄下的VM arguments中填入參數,再Apply,再run)

  每次在方法執行完畢的時候,虛擬機會自動釋放掉爲該棧所分配的空間,在棧中,對應着一個棧幀的出棧。虛擬機會自動分配與回收內存,因此效率比較高。

  最後做一下棧的總結:

  • 存放基本類型變量,局部變量,對象的引用;
  • 系統自動分配與回收內存,效率較高,快速,存取速度比堆要快;
  • 是一塊連續的內存的區域,有大小限制,如果超過了就會棧溢出,並拋出棧溢出的異常StackOverflowError;
  • Java會自動釋放掉爲該變量所分配的內存空間;

      棧又分爲java棧和本地方法棧。顧名思義,本地方法棧自然就是爲本地方法提供服務的,java棧是爲java服務的。
      注意,JVM棧是每個線程私有的!

  對象就存在圖中的內存區域,在JVM中,那片區域叫做堆!
    由圖中可以看到堆的存儲結構和棧是不同的,堆在內存中並不是一塊連續的區域,他是分散的(物理上是分散,但邏輯上是連續的,大家好好體會一下);虛擬機通過棧中引用的指引在堆中找到所需要的對象。

  在虛擬機遇到一條new的指令的時候,經過一系列的操作過後(現在講的話會看不懂)虛擬機就要爲該新生對象分配內存空間了,那麼問題來了,這麼散,虛擬機要怎麼知道如何分配呢?分配的方式有兩種:指針碰撞和空閒列表。

  指針碰撞是將內存邏輯上分爲兩邊,一邊是空閒的,一邊是在用的,指針指向分界點,當需要分配內存的時候只要移動指針即可。但這種只適用於內存規整的情況下,也就是剛剛說的分兩邊。一般用在Serial,PaeNew等垃圾收集器中,也就是堆中的新生代中。(最後一句話會在後面分幾章講,道路遙遠着!)

  那麼空閒列表說的就是在內存不是規整的情況下,虛擬機必須維護一個列表,用於記錄哪些內存是可用的,在需要進行分配的時候就從列表中找到一塊足夠大小的空間進行分配,並且更新列表。又要講一句看不懂的話:該方法適用於像CMS這種基於Mark-Sweep的垃圾收集器,適用於堆中的年老區!

  上兩段都提到了垃圾收集器,也就是GC。寫過java的都知道,java程序很少需要我們去自己釋放資源,原因就是這個GC機制了。

好啦,今天就不多說啦,好好理解一下上文,只要知道一下幾點就OK啦!

  • 存放new創建的對象和數組;
  • 在運行時動態分配內存(比如 new()),較慢,但靈活;
  • 是不連續的內存區域,在發出申請的時候,會幹嘛幹嘛的。。。。
  • 由Java虛擬機的自動垃圾回收器來管理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章