JAVA的內存結構

轉載: 原文連接:http://www.javashuo.com/article/p-oraotmci-bu.htmljava

一、運行時數據區

  • JVM所管理的內存包括如下幾個運行時數據區域,如圖所示 數組

  • 方法區和堆爲線程共享區,虛擬機棧、本地方法棧及程序計數器爲線程獨佔區。安全

程序計數器

  • 程序計數器是一塊較小的空間,它能夠看做是當前線程所執行的字節碼的行號指示器。 多線程

  • 若是線程執行的是java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址(能夠理解爲上圖所示的行號),若是正在執行的是native方法,這個計數器的值爲undefined。oop

  • JVM的多線程是經過線程輪流切換並分配CPU執行時間片的方式來實現的,任何一個時刻,一個CPU都只會執行一條線程中的指令。爲了保證線程切換後能恢復到正確的執行位置,每條線程都須要有一個獨立的程序計數器,各線程間的程序計數器獨立存儲,互不影響。佈局

  • 此區域是惟一一個在java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域,由於程序計數器是由虛擬機內部維護的,不須要開發者進行操做。spa

虛擬機棧

  • 每一個線程有一個私有的棧,隨着線程的建立而建立,生命週期與線程相同。.net

  • 虛擬機棧裏面存着的是一種叫「棧幀」的東西,每一個方法會建立一個棧幀,棧幀中存放了局部變量表、操做數棧、動態連接、方法出口等信息。線程

    • 局部變量表存放了編譯期可知的各類基本數據類型和對象引用類型。一般咱們所說的「棧內存」指的就是局部變量表這一部分。
    • 64位的long和double類型的數據會佔用2個局部變量空間,其他的數據類型只佔用1個。
    • 局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法須要在幀分配多少內存是固定的,運行期間不會改變局部變量表的大小。
  • 方法的調用到執行完畢,對應的就是棧幀的入棧和出棧的過程。指針

  • 棧的大小能夠固定也能夠動態擴展。

  • 在固定大小的狀況下,當棧調用深度大於JVM所容許的範圍,會拋出StackOverflowError異常。

  • 在動態擴展的狀況下,若擴展時沒法申請到足夠的內存,就會拋出OutOfMemoryError異常。

  • 棧內存溢出模擬

public class MainTest {

      public static void main(String[] args){
      	new MainTest().test();
      }

      private void test() {
      	System.out.println("run...");
      	test();
      }
  }
  • 報錯以下

  • Exception in thread "main" java.lang.StackOverflowError
    	at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
    	at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
  • 圖例:

本地方法棧

和虛擬機棧相似,二者的區別就是虛擬機棧是爲虛擬機執行java方法服務,本地方法棧爲虛擬機執行native方法服務 。

HotSpot虛擬機不區分虛擬機棧和本地方法棧,二者是一塊的。

與虛擬機棧同樣,本地方法棧也會拋StackOverflowError和OutOfMemoryError異常。

JVM管理的最大的一塊內存區域,存放着對象的實例,是線程共享區。

堆是垃圾收集器管理的主要區域,所以也被稱爲「GC堆」

JAVA堆的分類:

  • 從內存回收的角度上看,可分爲新生代(Eden空間,From Survivor空間、To Survivor空間)及老年代(Tenured Gen)
  • 從內存分配的角度上看,爲了解決分配內存時的線程安全性問題,線程共享的JAVA堆中可能劃分出多個線程私有的分配緩衝區(TLAB)

JAVA堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可。

可經過參數 -Xmx -Xms 來指定運行時堆內存的大小,堆內存空間不足也會拋OutOfMemoryError異常

方法區

方法區也是線程共享區,用於存儲【虛擬機加載的類信息(類的版本、字段、方法、接口),常量,靜態變量,即時編譯器編譯後的代碼等數據】

方法區邏輯上屬於堆的一部分,可是爲了與堆進行區分,一般又叫「非堆」。

HotSpot虛擬機使用永久代來實現方法區,使得HotSpot虛擬機的垃圾收集器能夠像管理堆內存同樣來管理這部份內存,能省去專門爲方法區編寫內存管理代碼工做。因此開發者喜歡將方法區稱爲永久代,本質上二者並不等價,對於其餘虛擬機來講不存在永久代的概念。

方法區可選擇不實現垃圾收集,通常來講,這個區域對內存回收的條件較爲苛刻,可是這部分區域的回收確實是必要的。

當方法區沒法知足內存分配需求時,將會拋OutOfMemoryError異常

運行時常量池

  • 運行時常量池是方法區的一部分。
  • class文件除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,用於存放編譯期生成的各類字面量和符號引用,這部份內容將在類加載後加入方法區的運行時常量池中存放。
  • 運行時常量池相於class文件中的常量池所不一樣的是其具有了動態性。class文件中常量池中的常量在編譯期間就已經定義好了,而運行時常量池在程序運行期間也能夠將常量放入該常量池中,最多見的作法就是調用String類的intern()方法。

二、對象的建立

一個對象的建立主要包括了一下幾大流程

在JVM在堆中爲對象分配內存階段,一般有如下兩種分配方式,虛擬機選擇哪一種分配方式是由JAVA堆是否規整決定的,而JAVA堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。

  • 1)指針碰撞:要求堆中內存絕對規整,全部用過的內存都放一邊,空閒的內存放在另外一邊,中間放着一個指針做爲分界點的指示器,分配內存就僅僅只是將該指針向空閒空間那邊挪動一段與對象大小相等的距離。
  • 2)空閒列表:針對的是堆中內存不規整的狀況,虛擬機維護着一個列表,記錄哪些內存塊是可用的,在分配時從列表中找到一塊足夠大的空間劃分給對象實例,並更新列表上的記錄。

在爲對象分配內存時,還須要考慮的一點就是線程安全性問題。可能出現正在給對象A分配內存,指針還沒來得及修改,對象B又同時使用了原來的指針來分配內存的狀況。針對這種問題,有如下兩種解決方案。

  • 1)對分配內存空間的動做進行同步處理,保證更新操做的原子性(採用CAS + 失敗重試機制保障原原子性),但效率較低。
  • 2)使用本地線程分配緩衝(TLAB),哪一個線程須要分配內存,就在哪一個線程的TLAB上分配,只有TLAB用完並須要分配新的TLAB時,才須要同步鎖定(可經過-XX:+/-UseTLAB參數來設定虛擬機啓用TLAB)。

三、對象的內存佈局

在HotSpot虛擬機中,對象在內存中存儲的佈局可分爲3塊區域:對象頭、實例數據和對齊填充。

  • 1)對象頭,由 Mark Word 和類型指針所組成。

Mark Word,用於存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標誌、線程持有鎖、偏向線程ID、偏向時間戳等。

  • Mark Word在32位的JVM中對應的長度是32bit,在64位的JVM中長度是64bit。
  • 因爲對象須要存儲的運行數據不少,其實已經超出了32位和64位的限制,爲了在極小的空間內存儲儘可能更多的信息,Mark Word 會根據對象狀態的不一樣來存儲不一樣的信息。如在32位的HotSpot虛擬機中,如對象處於未被鎖定的狀態,那麼 Mark Word 將使用25bit用於存儲對象的哈希碼,4bit用於存儲對象分代年齡,2bit用於存儲鎖標誌位,1bit固定爲0,其餘狀態以下表所示:

類型指針,虛擬機經過這個指針來肯定對象是哪一個類的實例,該指針指向對象的類元數據。(因爲查找對象的元數據信息並不必定要通過對象自己,因此並非全部的虛擬機實現都必須在對象數據上保留類型指針)

若是對象是一個JAVA數組,那麼在對象頭中還必須有一塊用於記錄數組長度的數據,由於普通對象的大小可經過元數據信息來獲取,而數組不行。

  • 2)實例數據,對象真正存儲的有效信息,也就是在代碼中所定義+的各類類型的字段內容。

    • 不管是從父類繼承下來的,仍是在子類中定義的,都須要記錄起來。
    • 存儲順序收虛擬機分配策略參數(FieldsAllocationStyle)和字段在java源碼中定義順序的影響。
    • HotSpot虛擬機默認的分配策略爲longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),相同帶寬的字段老是被分配到一塊兒,在此以後,父類中定義的變量會出如今子類以前。
    • 若是CompactFields參數的值爲true(默認爲true),那麼子類中較窄的變量也可能會插入到父類變量的空隙中。
  • 3)對齊填充,無特殊含義,僅僅起到佔位符的做用。HotSpot虛擬機要求起始地址必須是8字節的整數倍,所以當對象實例數據部分沒有對齊時,就須要經過對齊填充來補全。

相關文章
相關標籤/搜索