JVM內存結構

1、爲何要了解JVM內存管理機制

  • JVM自動的管理內存的分配與回收,這會在不知不覺中浪費不少內存,致使JVM花費不少時間去進行垃圾回收(GC)
  • 內存泄露,致使JVM內存最終不夠用

2、認識JVM基本結構

線程私有:html

  一、程序計數器:前端

  程序計數器是一個較小的內存空間,它能夠被當作當前線程所執行的字節碼的行號指示器。程序計算器保證了線程切換後能恢復到正確的執行位置。 若是線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的是Natvie方法,這個計數器值則爲空(Undefined)。在這裏,程序計數器是惟一一個在Java虛擬機規範中沒有規定OutOfMemoryError狀況的區域。java

  二、Java棧(全稱是java虛擬機棧):數組

  虛擬機棧棧描述的是Java方法執行的內存模型,每一個方法在執行的同時都會建立一個棧幀,用於存儲局部變量表、操做數棧,動態連接、方法出口等信息。每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧出棧的過程。服務器

       局部變量表:在變量槽中保存方法的參數和變量(即各類基本數據類型、對象引用類型),每一個變量槽 4 個字節佔 32 位,能夠是基本數據類型也能夠是對象引用。局部變量表大小在編譯時就已經肯定,long 和 double 類型數據會佔 2 個變量槽,其他的只佔用 1 個數據結構

  操做數棧:在執行指令的時候用於數據的存取,其大小也是在編譯時就已經肯定jvm

  動態連接:每一個棧幀都有它在運行時常量池中所屬方法的引用,在運行時會轉換爲直接引用jsp

  返回地址:當一個方法開始執行後,有兩種方式能夠退出這個方法:一是執行引擎遇到返回的字節碼指令;另外一種是在方法執行中出現了異常而且沒有在此方法中處理,這種退出不會返回給上層調用者任何值。不管如何退出,都須要繼續執行上層方法。在方法退出時可能的執行操做有:把返回值(若是有)壓入上層方法的棧幀的操做數棧中,調整 PC 寄存器指向方法調用指令的後一條指令。ui

      調節參數lua

  • -Xss:設置棧的大小,一般設置爲1m就好
  • <jvm-arg>-Xss1m</jvm-arg>

       在Java虛擬機規範中,對這個區域規定了兩種異常情況:若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError異常若是虛擬機棧能夠動態擴展(當前大部分的Java虛擬機均可動態擴展,只不過Java虛擬機規範中也容許固定長度的虛擬機棧),當擴展時沒法申請到足夠的內存時會拋出OutOfMemoryError異常。

  三、本地方法方法棧:

  本地方法棧(Native Method Stacks)與虛擬機棧所發揮的做用是很是類似的,其區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。虛擬機規範中對本地方法棧中的方法使用的語言、使用方式與數據結構並無強制規定,所以具體的虛擬機能夠自由實現它。甚至有的虛擬機(譬如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。與虛擬機棧同樣,本地方法棧區域也會拋StackOverflowError和OutOfMemoryError異常。

線程共享:

  一、Java堆:

  Java堆是被全部線程共享的一塊內存區域,在虛擬機啓動時建立

  Java堆是垃圾收集器管理的主要區域,所以不少時候也被稱作「GC堆」。

  Java堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可,就像咱們的磁盤空間同樣。在實現時,既能夠實現成固定大小的,也能夠是可擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(經過-Xmx和-Xms控制)。

  • 存放內容
    • 對象實例(類中的實例變量會隨着對象實例的建立一塊兒分配在堆中,固然如果基本數據類型的話,會隨着對象的建立直接壓入操做數棧)
    • 數組值
  • 使用實例
    • 全部經過new建立的對象都在這塊兒內存分配,具體分配到年輕代仍是年老代須要根據配置參數而定(新建對象直接分配到年老代有兩種狀況,看下邊)
  • 調節參數
    • -Xmx:最大堆內存,默認爲物理內存的1/4但小於1G
    • -Xms:最小堆內存,默認爲物理內存的1/64但小於1G
    • -XX:MinHeapFreeRatio,默認當空餘堆內存小於最小堆內存的40%時,堆內存增大到-Xmx
    • -XX:MaxHeapFreeRatio,當空餘堆內存大於最大堆內存的70%時,堆內存減少到-Xms
  • 注意點
    • 在實際使用中,-Xmx與-Xms配置成相等的,這樣,堆內存就不會頻繁的進行調整了
  • 拋出錯誤
    • OutOfMemoryError:在堆中沒有內存完成實例分配(關於實例內存的分配,以後再說),此時堆內存已達到最大沒法擴展時。
  • 堆內存劃分

    • 新生代
      • 組成:Eden+From(S0)+To(S1)
      • -Xmn:整個新生代的大小
      • -XX:SurvivorRatio:調整Eden:From(To)的比率,默認爲8:1
    • 年老代
      • 新建對象直接分配到年老代,兩種狀況
        • 大對象:-XX:PretenureSizeThreshold(單位:字節)參數來指定大對象的標準,在Parallel Scavenge GC下可能無效
        • 大數組:數組中的元素沒有引用任何外部的對象
  • 總結
    • 企業開發中,-Xmx==-Xms
    • 一般,-Xmx設置爲2048m就沒問題了,固然還要根據本身的程序去預估,並在運行過程當中去調整,這裏以在Resin服務器中配置爲例
    •             <jvm-arg>-Xms2048m</jvm-arg>
                  <jvm-arg>-Xmx2048m</jvm-arg>
                  <jvm-arg>-Xmn512m</jvm-arg>
                  <jvm-arg>-XX:SurvivorRatio=8</jvm-arg>
                  <jvm-arg>-XX:MaxTenuringThreshold=15</jvm-arg>        

       


      能夠看到,-Xms==-Xmx==2048m,年輕代大小-Xmn==512m,這樣,年老代大小就是2048-512==1536m,這個比率值得記住,在企業開發中,年輕代:年老代==1:3,而此時,咱們配置的-XX:MaxTenuringThreshold=15(這也是默認值),年輕代對象通過15次的複製後進入到年老代(關於這一點,在以後的GC機制中會說),

    • -XX:MaxTenuringThreshold與-XX:PretenureSizeThreshold不同,不要看錯

  二、方法區:

   方法區(Method Area)與Java堆同樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。雖然Java虛擬機規範把方法區描述爲堆的一個邏輯部分,可是它卻有一個別名叫作Non-Heap(非堆),目的應該是與Java堆區分開來。

  • 存放內容(類的信息、類static屬性、方法、常量池)
    • 已經加載的類的信息(名稱、修飾符等)
    • 類中的static變量
    • 類中的field信息
    • 類中定義爲final常量
    • 類中的方法信息
    • 運行時常量池:編譯器生成的各類字面量和符號引用(編譯期)存儲在class文件的常量池中,這部份內容會在類加載以後進入運行時常量池

     運行時常量池是方法區一個很是重要的區域,簡稱 RCP。首先咱們要知道在字節碼文件中,除了有類的字段、方法等信息描述外,還有常量池信息。常量池用來保存常量(字符串常量和 final 常量)與符號引用,這部份內容在被類加載後,會儲存到方法區中的 RCP 中,能夠說 RCP 是類中的常量池在運行時的表示形式。另外運行時產生的常量也能夠被放入常量池中,好比 String 的 intern() 方法,當常量池擴展時沒法申請到內存時會拋出 OutOfMemoryError 異常。

  • 使用實例:反射,在程序中經過Class對象調用getName等方法獲取信息數據時,這些信息數據來源於方法區。
  • 調節參數
    • -XX:PermSize:指定方法區的最小值,默認爲16M
    • -XX:MaxPermSize:指定方法區的最大值,默認爲64M
  • 所拋錯誤
    • 方法區域要使用的內存超過了其容許的大小時,拋出OutOfMemoryError
  • 內存回收的主要目標
    • 對類的卸載(這也是爲何不少企業使用velocity等模板引擎作前端而不是使用jsp的緣由之一)
    • 針對常量池的回收
  • 總結
    • 通常而言,在企業開發中,-XX:PermSize==-XX:MaxPermSize
    • 一般,這個大小設置爲256M就沒問題了,固然還要根據本身的程序去預估,並在運行過程當中去調整,這裏以在Resin服務器中配置爲例
    •  <jvm-arg>-XX:PermSize=256M</jvm-arg>
       <jvm-arg>-XX:MaxPermSize=256M</jvm-arg>
    •  類中的static變量會在方法區分配內存,可是類中的實例變量不會(類中的實例變量會隨着對象實例的建立一塊兒分配在堆中,固然如果基本數據類型的話,會隨着對象的建立直接壓入操做數棧)
    • 關於方法區的存放內容,能夠這樣去想全部的經過Class對象能夠反射獲取的都是從方法區獲取的(包括Class對象也是方法區的,Class是該類下全部其餘信息的訪問入口)  

注意:常量池在jdk1.6在方法區;在jdk1.7在堆

附:元數據區

  • 調節參數:-XX:MaxMetaspaceSize,若是不指定大小,極限狀況下可能耗盡系統全部內存
  • 元數據區是堆外的一塊直接內存

 

    備註: 部份內容轉自 https://blog.csdn.net/luanlouis/article/details/40043991   http://www.cnblogs.com/java-zhao/p/5179836.html

相關文章
相關標籤/搜索