基礎篇:JVM運行時內存佈局

1 JVM的內存區域佈局

  • java代碼的執行步驟有三點html

    • java源碼文件->編譯器->字節碼文件
    • 字節碼文件->JVM->機器碼
    • 機器碼->系統CPU執行
  • JVM執行的字節碼須要用類加載來載入;字節碼文件能夠來自本地文件,能夠在網絡上獲取,也能夠實時生成。就是說你能夠跳過寫java代碼階段,直接生成字節碼交由JVM執行
  • 其中Java虛擬機棧、程序計數器、Heap、本地方法棧、Metaspace屬於JVM運行時的內存;按是否線程共享則能夠分兩類

  • JAVA堆和MetasSpace元空間屬於線程共享的;虛擬機棧和本地方法棧、程序計數器是線程私有的

2 JVM五大數據區域介紹

  • 2.1 程序計數器(Progarm Counter Register)java

    • 一塊較小的內存空間, 是當前線程所執行的字節碼的行號指示器。線程有一個獨屬的程序計數器,字節碼解析工做時須要程序計數器來選取下一指令,分支、循環、跳轉等依賴它
    • 正在執行java方法線程的計數器記錄的是虛擬機字節碼指令的地址;若是仍是Native方法,則爲空
    • 程序計數器內存區域是惟一一個在虛擬機中沒有規定任何OutOfMemoryError錯誤的區域
  • 2.2 虛擬機棧(Virtual Machine Stack)

    • Java方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀(Stack Frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息
    • 每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程
    • 棧幀是用來存儲數據和部分過程結果的數據結構,同時也被用來處理動態連接(Dynamic Linking)、 方法返回值和異常分派(Dispatch Exception)。棧幀隨着方法調用而建立,隨着方法結束而銷燬(不管方法是正常完成仍是異常完成)
    • 若是線程請求的棧深度大於虛擬機容許深度,則拋出StackOverflowError;擴展時沒法申請到足夠內存,則拋出OutOfMemeryError
    • 2.3 本地方法棧(Native Method Stack)緩存

      • 本地方法棧和虛擬機棧做用相似,區別是虛擬機棧爲執行Java方法服務,而本地方法棧則爲Native方法服務。(HopShot的實現 直接把本地方法棧和虛擬機棧合二爲一)
    • 上述3類區域,生命週期與Thread相同,即:線程建立時,相應的內存區建立,線程銷燬時,釋放相應內存
    • 2.4 堆(Heap)網絡

      • 線程共享的一塊內存區域,幾乎全部的對象實例在這裏分配內存,也是垃圾收集器進行垃圾收集的最重要的內存區域。所以不少時候也叫GC堆
      • 線程私有的分配緩存區(Thread Local Alloaction Buffer)也是在堆劃分出來的
      • JDK8的版本,因使用元空間代替永久代,字符串常量池和類的靜態變量也放入java堆中

    • 2.5 元空間(MetaSpace)數據結構

      • 主要存儲類的元數據,好比類的各類描述信息,類名、方法、字段、訪問限制等,既編譯器編譯後的代碼等數據
      • 運行時常量池:Class文件中除了有類的版本、字段、方法等描述等信息外;還有一項信息是常量池,用於存放編譯期生成的各類字面量和符號引用,這部分將在類加載後存放到元空間的運行時常量池中
    • 使用元空間代替永久代緣由多線程

      • 永久代的大小是在啓動時固定好的,很難進行調優;太大則容易致使永久代溢出;過小在運行時,容易拋出OutOfMemeryError
      • 字符串存在永久代中,使用時易出問題,因爲永久代內存常常不夠用,爆出異常OutOfMemoryError: PermGen
    • CodeCache併發

      • JVM生成的native code存放的內存空間稱之爲Code Cache;JIT編譯、JNI等都會編譯代碼到native code,其中JIT生成的native code佔用了Code Cache的絕大部分空間
    • 直接內存函數

      • 它並非虛擬機運行時數據區的通常分,也不在規範定義。JDK1.4,引入了Channel(通道)與Buffer(緩存區)的I/O方式,它可使用Native函數分配堆外內存,可經過DirectByteBuffer操做。

    3 JVM運行時內存佈局和JMM內存模型區別

    • JVM內存區域是指JVM運行時將內存數據分區域存儲,強調對內存空間的劃分
    • JAVA內存模型是Java語言在多線程併發狀況下對於共享變量內存操做的規範:解決變量在多線程的可見性、原子性的問題

    4 JMM內存模型交互操做

    • 內存交互操做有八種,虛擬機的實現保證每個操做都是原子性的佈局

      • lock(鎖定):做用於主內存的變量,標識變量爲線程獨佔狀態
      • unlock(解鎖):做用於主內存的變量,釋放一個處於鎖定狀態的變量,釋放後的變量才能夠被其餘線程鎖定
      • read(讀取):做用於主內存變量,從主內存中讀取出後面load操做要用到的變量
      • load(載入):做用於主內存中的變量,把剛纔read的值放入工做內存的副本中
      • use(使用):做用於工做內存中的變量,當線程執行某個字節碼指令須要用到相應的變量時,把工做內存中的變量副本傳給執行引擎
      • assign(賦值):做用於工做內存中的變量,把一個從執行引擎中接受到的值放入工做內存的變量副本中
      • store(存儲):做用於工做內存中的變量,把工做內存中的變量送到主內存,給後續的write使用
      • write(寫入):做用於主內存中的變量,把store的工做內存中的變量值,寫入主內存中
    • read和load 好像是相同的操做?各位有何高見,請指教下
    • JMM對這八種指令的使用,制定了以下規則大數據

      • read和load、store和write必須順序執行,並且兩個指令綁定出現;就是說出現read就要有load
      • 不容許一個線程丟棄最近的assign操做,工做內存中的變量改變後,必須write同步到主內存
      • 不容許一個線程把沒有發生assign操做的變量同步到主內存
      • 新的變量必須誕生於主內存,不容許工做內存使用一個沒有初始化的變量;use、store操做變量以前,必須通過load和assign操做
      • 變量同一時刻只容許一個線程對其lock,該線程能夠對該變量加鎖屢次,釋放鎖須要執行相同次數的unlock,lock和unlock要成對出現
      • 一個變量沒有lock,不能unlock;而且一個線程不能unlock被其餘線程鎖住的變量
      • 執行unlock前,必須把工做內存中的變量同步到主內存中
      • 執行lock操做,須要清空工做內存(全部),而且須要使用該變量以前,要從新執行load和assign操做

    歡迎指正文中錯誤

    關注公衆號,一塊兒交流

    參考文章

    相關文章
    相關標籤/搜索