一文了解 JAVA 堆
一、前言

眾所周知,在Java中內存主要分為以下幾類:
寄存器:最快的存儲區, 由編譯器根據需求進行分配,我們在程序中無法控制。
棧:存放基本類型的變量數據和對象的引用,但對象本身不存放在棧中,而是存放在堆(new 出來的對象)或者常量池中(字符串常量對象存放在常量池中。)
堆:存放所有new出來的對象。
靜態域:存放靜態成員(static定義的)。
常量池:存放字符串常量和基本類型常量(public static final)。
非RAM存儲:硬盤等永久存儲空間
這里我們主要關心棧,堆和常量池,對于棧和常量池中的對象可以共享,對于堆中的對象不可以共享。棧中的數據大小和生命周期是可以確定的,當沒有引用指向數據時,這個數據就會消失。堆中的對象的由垃圾回收器負責回收,因此大小和生命周期不需要確定,具有很大的靈活性。
對于字符串:其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(直接用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的)才能確定的就存儲在堆中。對于equals相等的字符串,在常量池中永遠只有一份,在堆中有多份。
在前期博文《JVM進階(一):初識Java棧》中我們說到了棧,棧在內存中是連續的空間;保存一個個的棧幀,對應一次次方法的調用;還講到了棧是保存對象的引用,那么對象存在哪里呢?
二、堆存儲
我們來看看下面的一張圖:
對象就存在圖中的內存區域,在JVM中,那片區域叫做堆!
由上圖可以看到堆的存儲結構和棧是不同的,堆在內存中并不是一塊連續的區域,堆是分散的(物理上分散,但邏輯上是連續的,這里大家好好體會一下);虛擬機通過棧中引用的指引在堆中找到所需要的對象。
在虛擬機遇到一條new指令的時候,經過一系列的操作過后,虛擬機就要為該新生對象分配內存空間了,那么問題來了,這么散,虛擬機要怎么知道如何分配呢?分配的方式有兩種:指針碰撞和空閑列表。
指針碰撞是將內存邏輯上分為兩邊,一邊是空閑的,一邊是在用的,指針指向分界點,當需要分配內存的時候只要移動指針即可。但這種只適用于內存規整的情況下,也就是剛剛說的分兩邊。一般用在Serial,PaeNew等垃圾收集器中,也就是堆中的新生代中。
空閑列表說的就是在內存不規整的情況下,虛擬機必須維護一個列表,用于記錄哪些內存是可用的,在需要進行分配的時候就從列表中找到一塊足夠大小的空間進行分配,并且更新列表。該方法適用于像CMS這種基于Mark-Sweep的垃圾收集器,適用于堆中的年老區!
上兩段都提到了垃圾收集器,也就是GC。寫過java應用的都知道,java程序很少需要我們去自己釋放資源,原因就在于這個GC機制了。
三、總結
存放new創建的對象和數組;
在運行時動態分配內存(比如 new()),較慢,但靈活;
是不連續的內存區域,在發出申請的時候,會干嘛干嘛的。。。。
由Java虛擬機的自動垃圾回收器GC來管理。
四、拓展閱讀
《JVM虛擬機專欄》
Java JVM
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。