JVM:一、運行時數據區域

概述

    Java的虛擬機使Java的成爲了一種跨平臺的語言,Java的不直接與操作系統接觸,而是通過虛擬機這個中間橋樑,通過JVM與底層接觸。不同的系統有不同的JVM,但是所有的這些JVM都完美的支持Java語法,這就使得write once,run everywhere成爲可能。

    除此之外,JVM的內存管理機制使得不需要再爲每一個新的操作去刪除/免費代碼,由機器代替程序員這樣就不容易出現內存泄露和內存溢出的問題了,但是一旦出現了這種問題如果不瞭解JVM是怎樣使用內存的,那麼排查錯誤將會非常困難。

JVM運行時數據區域

  • 程序計數器
  • 虛擬機棧
  • 本地方法棧
  • 方法區

有的區域隨着虛擬機進程的啓動而存在,有的區域則依賴用戶進程的啓動和結束而創建和銷燬。

1. 程序計數器

    解釋:【指向當前線程所執行的字節碼的行號】,其實就是一小塊內存,記錄着當前程序運行到哪了字節碼解釋器的工作就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令(字節碼指令是什麼?)。分支,循環,跳轉,異常處理,線程回覆等都需要依賴這個計數器來完成。

    由於Java的多線程是通過線程輪流切換完成的,一個線程沒有執行完時就需要一個東西記錄它執行到哪了,下次搶佔到了CPU資源時再從這開始,這個東西就是程序計數器,正是因爲這樣,所以它也是“線程私有”的內存。

    如果一個線程執行一個主要方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是一個本地方法,這個計數器的值則爲空,此內存區域是唯一一個在Java的虛擬機規範中沒有規定任何OutOfMemoryError異常情況的區域。

2. Java虛擬機棧

    與程序計數器一樣,Java的虛擬機棧也是線程私有的,虛擬機棧描述的是Java的方法執行的內存模型,方法每個執行在同時的創建³³都會一個棧楨用於存儲局部變量表,操作數棧,動態鏈接,方法出口等信息,下圖爲棧楨結構圖:

    通常所說的JVM裏的堆和棧裏這個棧就是Java的虛擬機棧,或者說是Java的虛擬機棧中的局部變量表部分。

    局部變量表存放了編譯期可知的各種基本數據類型,對象引用(僅限局部變量的,不包含成員變量的)。其中每個局部變量空間(Slot)有32位,所以long和double類型的數據會佔用兩個局部變量空間,其他類型包括對象引用佔用一個。對象引用調用的是存在堆中的對象,這個引用可以是對象的起始地址或者是指向對象的句柄。局部變量表所需的內存在編譯期就已經確定了也就是進入這個方法時就已經確定了,運行期間不會更改。

    操作數棧則存儲方法內一些進行了運算操作後的結果。

    動態鏈接,在方法內調用接口,通過字面量鏈接到具體的實現類,實現Java的動態特性。

    方法出口(返回地址),return或者發生Exception等。

    如果方法methodOne方法調用了methodTwo,那麼methodOne就會先入棧創建一個棧楨,接着methodTwo再入棧成爲棧頂(假設沒有其他的方法執行),methodTwo執行完先出棧,接着methodOne執行完出棧。

    在使用遞歸的情況下,如果線程請求的棧的深度超過虛擬機所允許棧的深度就會拋出StackOverflowError;但是大部分虛擬機棧的深度都可以動態擴展,HotSpot中使用XSS可以設置棧的深度,如果擴展時無法請求到足夠的內存就會拋出OutOfMemoryError。

3. 本地方法棧

    本地方法棧和虛擬機棧相似,區別就是虛擬機爲虛擬機棧執行Java服務(字節碼服務),而本地方法棧爲虛擬機使用到的Native方法服務。本地方法棧中使用的語言,使用方式,數據結構沒有強制要求。

4. Java堆

    堆是JVM裏最大的一塊內存區域,被所有線程共享,在虛擬機啓動時創建,此區域的目的就是存放對象實例和數組,幾乎所有的對象實例都在這分配(隨着JIT的發展已經不是那麼絕對了)的.java堆是垃圾收集管理的主要區域,由於現在收集器基本都採用分代收集方法,所以Java的堆中還可以分爲新生代,老年代,永久代.1.8之後取消了永久代;其中新生代又劃分爲Eden空間,From Survivor空間,To Survivor空間。無論怎麼劃分都是爲了更好的回收,分配,利用內存。下圖爲1.8後的內存模型

    根據的Java虛擬機規範,Java的堆可以處於物理不連續的空間中,只要邏輯連續即可。在實現時,既可以實現成固定大小的也可以是可擴展的(通過-Xmx和-Xms控制),如果堆中沒有足夠的內存完成實例分配,並且堆也無法得到擴展時,將會拋出的OutOfMemoryError異常。

5. 方法區

    方法區也是一個線程共享的區域,存儲已被虛擬機加載的類信息,常量(final),靜態變量(static),JIT(即時編譯器)編譯後的代碼等數據。

Java虛擬機規範把方法區描述爲堆的一個邏輯部分,其實堆和方法區可以看成數據部分;虛擬機棧和程序計數器可以看成指令部分;方法區存儲一些不會變更的數據,之前熱點上使用GC分代收集管理方法區,所以方法區也被稱爲永久代(本質上兩者不等價),但是現在已經使用Native Memory來代替永久代了。

    虛擬機對方法區規範非常寬鬆,除了和Java的堆一樣不需要連續的內存和可以選擇固定大小意外,還可以選擇不實現垃圾回收。垃圾回收行爲在這個區域比較少見但還是有必要的,主要是針對常量池回收和類型的卸載。

5.1運行時常量池

    運行時常量池是方法區的一部分,用於存放編譯期生成的各種字面量和符號引用,運行時常量池相對於類常量池另外一個特性就是具備動態性,運行期間可能將新的常量放入池中。 

 

 

參考資料:《深入理解Java虛擬機》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章