1. 程式人生 > >JVM(二)記憶體區域的理解

JVM(二)記憶體區域的理解

一 JVM的記憶體區域結構

1.1

由前面文章所述JVM是用來將編譯後的java指令生成.class檔案然後在不同的平臺上解釋為不同的平臺指令,然而這一切是怎樣實現的呢? 首先我們需要知道JVM的記憶體區域結構: 在這裡插入圖片描述 由上節所知上面會有類載入器,通過類載入器執行(載入-連線(驗證-準備-解析)-初始化)到它的記憶體區域然後通過記憶體區域進行一系列操作。

2.2

執行時資料區就是JVM執行期間對JVM記憶體空間的劃分和分配,資料儲存被劃分為五個區,通過類載入器的程式都被分配在執行時資料區根據類別分配到這五個區: 1堆 2方法區 3虛擬機器棧 4 程式計數器 5本地方法棧

二. 五個區域詳解

2.1 方法區

從上圖可以看出,方法區是被所有執行緒共享的區域,它主要用來儲存靜態變數,已被載入的類的資訊,常量,和即時編譯器(就是將位元組碼檔案解釋為不同平臺指令)編譯後的程式碼,方法區主要儲存每個類的結構資訊,例如成員變數,方法資料,和建構函式和普通函式的程式碼塊內容。包括類,介面,例項初始化特殊方法。 執行時常量池也是方法區的一部分,常量池主要是用來存放編譯器生成的各種字面變數和符號引用,直接應用也會儲存在執行時常量池中。

2.2 堆

  1. 堆是JVM記憶體區域中最大的一部分,隊也是所有的執行緒所共享的記憶體區域。
  2. 此記憶體的目的就是為了存放物件例項,幾乎所有的物件的例項和陣列都存放在堆中
  3. java的堆也是垃圾收集器管理的主要區域,從記憶體回收的角度看,java堆會被劃分為新生代和老年代,從記憶體分配的角度來看,執行緒共享的java堆劃分為多個執行緒的私有的分配緩衝區。

2.3 程式計數器

  1. 程式計數器佔用很小的一塊記憶體,它是屬於執行緒的私有空間,是執行當前位元組碼的行號指示器,位元組碼直譯器的解釋工作就是通過選擇這個程式計數器的行號來進行的,選取下一條要進行的指令。比如迴圈,跳轉,分支,異常處理等,
  2. 由於java虛擬機器多執行緒是併發進行的,所以每個執行緒都需要自己的獨立的程式計數器,這樣才能保證多執行緒的併發進行(輪流切換並分配處理器執行時間,這樣就能保證一個執行緒正在執行,另一個執行緒執行,恢復時有自己的程式計數器這樣就能恢復到原來的地方,所以每個執行緒的程式計數器都是獨立存在的,互不影響。 6.若執行緒正在執行一個java方法,則程式計數器儲存的是下一步指向的指令,若是native方法則儲存的是空。

2.4 java虛擬機器棧

  1. java虛擬機器棧和程式計數器是相同的,都是執行緒的私有空間,它和執行緒有著相同的生命週期,它描述的是java方法的執行的記憶體模型,每個方法執行時都會建立一個棧幀存放與方法有關的變數,如區域性變數,運算元幀,動態連結,方法出口等,每個方法執行到結束,其實也就是棧幀在虛擬機器中從入棧到出棧的過程。
  2. 區域性變量表中存放的是基本資料型別或者物件引用型別或者returnAddress(位元組碼指令的地址)
  3. 區域性變數空間分配:long和double是佔用兩個區域性變數空間大小,其它的資料型別都只佔用一個區域性變數空間,而且這些空間實在編譯期間都已經分配好了,在後面的方法執行時期是不會在改變區域性變量表的大小。
  4. 這個區域會有兩種異常:stackOverFlowError:執行緒請求的棧的深度大於虛擬機器所允許的深度就會出現這個錯誤;現在的大多數虛擬機器都是支援動態擴充套件的(多個執行緒申請記憶體),若是擴充套件時也無法申請到足夠的記憶體,就會出現OutofMemory
  5. 作業系統對每個程序分配的記憶體有大小限制(windows64位系統中最大是2G記憶體),對於java虛擬機器,最大是2G的記憶體,不算java虛擬機器程序啟動時佔的記憶體,剩下的記憶體都分配給了共享區和執行緒獨享區,也就是堆記憶體+方法區記憶體+執行緒棧記憶體(程式計數器忽略不及),那麼不論堆記憶體和方法區記憶體佔的記憶體空間有多小,執行緒棧記憶體的大小總是有限制的,由於java虛擬機器會為每個執行緒分配一塊記憶體,如果執行緒數量足夠多,當再申請新的記憶體時,發現無記憶體可用了,就會丟擲OutofMemoryError。

2.5 本地方法棧

本地方法棧和JVM虛擬機器棧的作用非常相似,只不過虛擬機器棧主要是執行java方法,而本地方法棧執行的是native方法。他也會丟擲OutOfMemory和StackOverFlow異常

閱讀《深入瞭解JVM》 綜合另外一些部落格