1. 程式人生 > >jvm記憶體分配及物件建立和回收過程

jvm記憶體分配及物件建立和回收過程

Java歷史

2004.9 jdk1.5 tiger 自動裝箱拆箱,泛型,,註解,列舉,變長引數,增強for迴圈 spring2.x spring4.x

2006 jdk1.6 javaee Javase Javame jdk6

  1. 提供指令碼支援
  2. 提供編譯api以及http伺服器api

2009 jdk1.7 收購sun 74億

2014 jdk1.8

2017 jdk1.9

2018 jdk10

java 技術體系

Java程式設計語言

java 虛擬機器

class 類檔案格式

Java API

第三方Java類庫

Java8新特性

  1. 介面預設方法和靜態方法
  2. lambda表示式和函數語言程式設計
  3. dateAPI
  4. 重複註解
  5. nashorn JavaScript引擎

jvm視覺化監控工具

jconsole.exe

在jdk/bin目錄下,雙擊開啟可執行,監控嗎某個Java程式的狀態

編寫測試類觀察jvm記憶體

JconsoleTest.java

package jconsole;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Veng Su [email protected]
 * @date 2018/4/29 11:28
 */
public class JconsoleTest
{
public static void main(String[] args) throws InterruptedException { Thread.sleep(5000); fill(1000); } private static void fill(int n) throws InterruptedException { List<JconsoleTest> jconsoleTests =new ArrayList<JconsoleTest>(); for (int i=0;i<n;i++){ Thread.sleep(200
); jconsoleTests.add(new JconsoleTest()); } } }

jvm記憶體溢位

Main.java

import java.util.ArrayList;
import java.util.List;

/**
 * @author Veng Su [email protected]
 * @date 2018/4/29 10:39
 */
public class Main {
    public static void main(String[] args) {
//        測試堆記憶體溢位
        List<Demo> demoList=new ArrayList<Demo>();
        while (true){
            demoList.add(new Demo());
        }
    }
}

Demo.java

import java.util.ArrayList;
import java.util.List;

/**
 * @author Veng Su [email protected]
 * @date 2018/4/29 10:39
 */
public class Main {
    public static void main(String[] args) {
//        測試堆記憶體溢位
        List<Demo> demoList=new ArrayList<Demo>();
        while (true){
            demoList.add(new Demo());
        }
    }
}

丟擲的異常:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3210)
    at java.util.Arrays.copyOf(Arrays.java:3181)
    at java.util.ArrayList.grow(ArrayList.java:265)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
    at java.util.ArrayList.add(ArrayList.java:462)
    at Main.main(Main.java:13)

jvm引數

匯出堆記憶體

-XX:+HeapDumpOnOutOfMemoryError -Xms20m -Xmx20m

Java虛擬機器的記憶體管理

執行時資料區

執行緒共享區

方法區

​ 執行時常量池

Java堆

執行緒獨立區

虛擬機器棧

​ 存放方法執行時所需的資料,稱為棧幀

本地方法棧

​ 為jvm呼叫到的native,即本地方法服務

程式計數器

​ 記錄當前執行緒執行到位元組碼的行號

程式計數器

  1. 如果執行緒執行的是Java程式碼,這個計數器記錄的正在執行的虛擬機器位元組碼指令的地址,如果正在執行的native方法,這個計數器的值為undefined
  2. 此區域是唯一一個在Java虛擬機器規範中沒有規定任何的OutOfMemoryError的情況的區域

Java虛擬機器棧

  1. 這個描述的是Java方法執行的動態記憶體模型

  2. 棧幀:每個方法執行都會建立一個棧幀,伴隨方法從建立到執行完成,用於儲存區域性變量表,運算元棧,動態連結,方法出口等

  3. 區域性變量表:存放編譯期已知的各種基本資料型別,引用型別,returnAddress型別
    區域性變量表的記憶體空間在編譯期完成分配,在進入一個方法時,這個方法需要在幀分配多少記憶體是固定的,在方法執行期間是不會改變的

  4. 虛擬機器棧的大小
    可能存在StackOverFlowError

    OutOfMemoryError記憶體不足,申請不到記憶體空間了

本地方法棧

Java虛擬機器棧為虛擬機器執行Java方法服務

本地方法棧為虛擬機器執行native方法服務

Java堆

存放物件例項

垃圾蒐集器管理的主要區域

新生代,老年代,Eden空間

申請不到空間同樣丟擲outofmemoryerror

方法區

儲存虛擬機器載入的類資訊,常量,靜態變數,及時編譯器編譯後的程式碼等資料

​ 類資訊:

​ 類的版本

​ 欄位

​ 方法

​ 介面

方法區和永久代 Hotspot使用永久代實現方法區,兩者不等價

垃圾回收在方法區的行為

異常的定義

申請空間失敗丟擲outofmemoryerror

執行時常量池

常量池相當於一個hashset存放這寫常量,

而new 出來的例項肯定是放到堆記憶體中去

執行時常量和位元組碼常量的區別,執行時建立的常量和編譯期建立的常量的區別

直接記憶體

物件的建立

這裡寫圖片描述

給物件分配記憶體的方法

  1. 指標碰撞
  2. 空閒列表

可能會出現執行緒安全性問題

如何解決

執行緒同步 缺點:效率低

本地分配緩衝

物件的結構

  1. header (物件頭)

    自身執行時資料(MarkWord)

    ​ 雜湊值 GC分代年齡 鎖狀態標誌 執行緒持有的鎖 偏向執行緒ID 偏向時間戳

    型別指標、

    這裡寫圖片描述

  2. instanceData

  3. Padding

    佔位符填充8的整數倍的作用

物件的訪問定位

  1. 使用控制代碼
    定位控制代碼池,在找到物件地址
  2. 直接指標
    直接找到物件地址 效能高 Hotspot使用直接地址定位

    其他虛擬機器

Sun hotshot

Bea JRockit

IBM J9

虛擬機器的發展

  1. sun classic vm

    • 世界山第一個商用虛擬機器
    • 只能使用純直譯器的方式執行java程式碼
  2. exact vm

    • exact memory management 準確試記憶體管理
    • 編譯器和直譯器混合工作以及兩級及時編譯器
    • 只在 Solaris平臺釋出
  3. hotspot vm

  4. kvm(kilobyte)

  5. JRocket
    BEA

    世界上最快的虛擬機器

    專注伺服器端的應用

    優勢

    ​ 垃圾蒐集器

    ​ MissionControll服務套件 尋找執行時的記憶體洩露的問題

  6. J9

    IBM Technology for Java virtual machine

  7. Azul vm

  8. Liquid vm

  9. Dalvik vm
    不是Java虛擬機器

    暫存器架構,非棧架構
    Google的

  10. Microsoft jvm
    只能執行在windows平臺下

  11. taobaovm
    深度定製

垃圾回收

如何判定物件為垃圾物件

  1. 引用計數法
    在物件中新增一個引用計數器,當有地方引用這個物件的時候,引用計數器的值就加1,當引用失效的時候,計數器的值就減1
    -verbose :gc -XX:+PrintGCDetails
    列印垃圾回收的資訊
  2. 可達性分析法
    GCRoot物件
    1. 虛擬機器棧
    2. 方法區類屬性所引用的物件
    3. 方法區常量所引用的物件
    4. 本地方法棧所引用的物件

如何回收

  1. 回收策略

    1. 標記-清除演算法
      效率問題

      空間問題

    2. 複製演算法

      1. 新生代
        1. Eden 伊甸園
        2. survivor 存活區
        3. Tenured Gen
      2. 老年代

      虛擬機器棧
      本地方法棧
      程式計數器

    3. 標記-整理-清除演算法
      針對老年代

    4. 分代收集演算法

  2. 垃圾回收器

    1. Serial
      單執行緒

    2. Parnew

    3. parallel scanvenge收集器
      -XX:MaxGFPauseMillis 垃圾收集器停頓時間1
      -XX:CGTimeRatio 吞吐量大小
      複製演算法(新生代收集器)

      多執行緒收集器
      達到可控吞吐量
      吞吐量:CPU執行程式碼的時間與CPU消耗的總時間的比值

    4. CMS收集器 current Mark sweep

      1. 工作過程
        1. 初始標記
        2. 併發標記
        3. 重新標記
        4. 併發清理
      2. 優點
        1. 併發收集
        2. 低停頓
      3. 缺點
        1. 佔用大量CPU資源
        2. 無法處理浮動垃圾
        3. 出現current mode failure
        4. 空間碎片
    5. G1

記憶體分配

1525344679957

只要電腦執行記憶體大於2g,CPU核心是多核, 預設是ServerVM

可以看到我們的虛擬機器是HotSpot

記憶體分配策略

  1. 優先分配到新生代的Eden區
    VM option

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20m -Xmx20m -Xmn10m -XX:SurvivorRatio=8

  2. 大物件直接進入到老年代
    指定進入老年代的物件的記憶體大小
    -XX:PretenureSizeThreshold

  3. 長期存活的物件分配到老年代
    -XX:MaxTenuringThreshold
    具有年齡計數器。每次回收時存活,年齡加1.到達閾值就進入老年代中

  4. 空間分配擔保

    如果記憶體空間不足,向擔保借;

    -XX:(+/-)HandlePromotionFailure

  5. 逃逸分析和棧上分配
    通過逃逸分析,分析出沒有逃逸的物件,直接在棧上分配空間。
    什麼是逃逸分析?
    分析物件的作用域。如果物件只有在方法體內有效,則判定為沒有逃逸。否則,為逃逸物件

  6. 動態物件年齡判斷

虛擬機器工具

  • JPS
    JAVA PROCESS STATUS

    JPS 名稱: jps - Java Virtual Machine Process Status Tool

    命令用法: jps options hostid

    ​ options:命令選項,用來對輸出格式進行控制

    ​ hostid:指定特定主機,可以是ip地址和域名, 也可以指定具體協議,埠。

    **功能描述: j**ps是用於檢視有權訪問的hotspot虛擬機器的程序. 當未指定hostid時,預設檢視本機jvm程序,否者檢視指定的hostid機器上的jvm程序,此時hostid所指機器必須開啟jstatd服務。 jps可以列出jvm程序lvmid,主類類名,main函式引數, jvm引數,jar名稱等資訊。

    1. 沒新增option的時候,預設列出VM標示符號和簡單的class或jar名稱
    2. -p :僅僅顯示VM 標示,不顯示jar,class, main引數等資訊.
    3. -m:輸出主函式傳入的引數. 下的hello 就是在執行程式時從命令列輸入的引數
    4. -l: 輸出應用程式主類完整package名稱或jar完整名稱.
    5. -v: 列出jvm引數, -Xms20m -Xmx50m是啟動程式指定的jvm引數
    6. -V: 輸出通過.hotsportrc或-XX:Flags=指定的jvm引數
    7. -Joption:傳遞引數到javac 呼叫的java lancher.
  • JSTAT
    jstat命令可以類裝載,記憶體,垃圾收集,jit編譯。命令的格式如下:

    jstat [-命令選項] [vmid] [間隔時間/毫秒] [查詢次數]
    1. 類載入統計
      jstat -class

      Loaded:載入class的數量
      Bytes:所佔用空間大小
      Unloaded:未載入數量
      Bytes:未載入佔用空間
      Time:時間
    2. 編譯統計
      jstat -compiler

      Compiled:編譯數量。
      Failed:失敗數量
      Invalid:不可用數量
      Time:時間
      FailedType:失敗型別
      FailedMethod:失敗的方法
    3. 垃圾回收統計
      jstat -gccapacity

      S0C:第一個倖存區的大小
      S1C:第二個倖存區的大小
      S0U:第一個倖存區的使用大小
      S1U:第二個倖存區的使用大小
      EC:伊甸園區的大小
      EU:伊甸園區的使用大小
      OC:老年代大小
      OU:老年代使用大小
      MC:方法區大小
      MU:方法區使用大小
      CCSC:壓縮類空間大小
      CCSU:壓縮類空間使用大小
      YGC:年輕代垃圾回收次數
      YGCT:年輕代垃圾回收消耗時間
      FGC:老年代垃圾回收次數
      FGCT:老年代垃圾回收消耗時間
      GCT:垃圾回收消耗總時間
    4. 新生代垃圾回收統計
      jstat -gcnew

      S0C:第一個倖存區大小
      S1C:第二個倖存區的大小
      S0U:第一個倖存區的使用大小
      S1U:第二個倖存區的使用大小
      TT:物件在新生代存活的次數
      MTT:物件在新生代存活的最大次數
      DSS:期望的倖存區大小
      EC:伊甸園區的大小
      EU:伊甸園區的使用大小
      YGC:年輕代垃圾回收次數
      YGCT:年輕代垃圾回收消耗時間
    5. 新生代記憶體統計
      jstat -gcnewcapacity

      NGCMN:新生代最小容量
      NGCMX:新生代最大容量
      NGC:當前新生代容量
      S0CMX:最大幸存1區大小
      S0C:當前倖存1區大小
      S1CMX:最大幸存2區大小
      S1C:當前倖存2區大小
      ECMX:最大伊甸園區大小
      EC:當前伊甸園區大小
      YGC:年輕代垃圾回收次數
      FGC:老年代回收次數
    6. 老年代垃圾回收統計
      jstat -gcold

      MC:方法區大小
      MU:方法區使用大小
      CCSC:壓縮類空間大小
      CCSU:壓縮類空間使用大小
      OC:老年代大小
      OU:老年代使用大小
      YGC:年輕代垃圾回收次數
      FGC:老年代垃圾回收次數
      FGCT:老年代垃圾回收消耗時間
      GCT:垃圾回收消耗總時間
    7. 老年代記憶體統計
      jstat -gcoldcapacity

      OGCMN:老年代最小容量
      OGCMX:老年代最大容量
      OGC:當前老年代大小
      OC:老年代大小
      YGC:年輕代垃圾回收次數
      FGC:老年代垃圾回收次數
      FGCT:老年代垃圾回收消耗時間
      GCT:垃圾回收消耗總時間
    8. 元資料空間統計
      jstat -gcmetacapacity

      MCMN:最小元資料容量
      MCMX:最大元資料容量
      MC:當前元資料空間大小
      CCSMN:最小壓縮類空間大小
      CCSMX:最大壓縮類空間大小
      CCSC:當前壓縮類空間大小
      YGC:年輕代垃圾回收次數
      FGC:老年代垃圾回收次數
      FGCT:老年代垃圾回收消耗時間
      GCT:垃圾回收消耗總時間
    9. 總結垃圾回收統計
      jstat -gcutil

      S0:倖存1區當前使用比例
      S1:倖存2區當前使用比例
      E:伊甸園區使用比例
      O:老年代使用比例
      M:元資料區使用比例
      CCS:壓縮使用比例
      YGC:年輕代垃圾回收次數
      FGC:老年代垃圾回收次數
      FGCT:老年代垃圾回收消耗時間
      GCT:垃圾回收消耗總時間
    10. JVM編譯方法統計
      jstat -printcompilation

      Compiled:最近編譯方法的數量
      Size:最近編譯方法的位元組碼數量
      Type:最近編譯方法的編譯型別。
      Method:方法名標識。
  • JINFO
    jinfo是jdk自帶的命令,用來檢視jvm的配置引數。通常會先使用jps檢視java程序的id,然後使用jinfo檢視指定pid的jvm資訊
    檢視jvm的引數

    jinfo -flags process_id
    檢視java系統引數
    jinfo -sysprops process_id

  • JMAP
    JVM Memory Map命令用於生成heap dump檔案,如果不使用這個命令,還可以使用-XX:+HeapDumpOnOutOfMemoryError引數來讓虛擬機器出現OOM的時候自動生成dump檔案。

    引數
    
    option:選項引數,不可同時使用多個選項引數
    
    pid:java程序id,命令ps -ef | grep java獲取
    
    executable:產生核心dump的java可執行檔案
    
    core:需要列印配置資訊的核心檔案
    
    remote-hostname-or-ip:遠端除錯的主機名或ip
    
    server-id:可選的唯一id,如果相同的遠端主機上運行了多臺除錯伺服器,用此選項引數標識伺服器
    
    
    
    options引數
    
    heap : 顯示Java堆詳細資訊
    histo : 顯示堆中物件的統計資訊
    permstat :Java堆記憶體的永久儲存區域的類載入器的統計資訊
    finalizerinfo : 顯示在F-Queue佇列等待Finalizer執行緒執行finalizer方法的物件
    dump : 生成堆轉儲快照
    F : 當-dump沒有響應時,強制生成dump快照
    -dump
    dump堆到檔案,format指定輸出格式,live指明是活著的物件,file指定檔名
    -heap
    列印heap的概要資訊,GC使用的演算法,heap的配置及使用情況,可以用此來判斷記憶體目前的使用情況以及垃圾回收情況
    -finalizerinfo
    
    列印等待回收的物件資訊,
    -histo
    
    列印堆的物件統計,包括物件數、記憶體大小等等。jmap -histo:live 這個命令執行,JVM會先觸發gc,然後再統計資訊
    jmap -histo:live 24971 | grep com.yuhuo 查詢類名包含com.yuhuo的資訊
    
    jmap -histo:live 24971 | grep com.yuhuo > histo.txt 儲存資訊到histo.txt檔案
    -permstat
    
    列印Java堆記憶體的永久區的類載入器的智慧統計資訊。對於每個類載入器而言,它的名稱、活躍度、地址、父類載入器、它所載入的類的數量和大小都會被列印。此外,包含的字串數量和大小也會被列印。
    -F
    
    強制模式。如果指定的pid沒有響應,請使用jmap -dump或jmap -histo選項。此模式下,不支援live子選項。
  • JHAT

    JVM Heap Analysis Tool命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內建了一個微型的HTTP/HTML伺服器,生成dump的分析結果後,可以在瀏覽器中檢視。在此要注意,一般不會直接在伺服器上進行分析,因為jhat是一個耗時並且耗費硬體資源的過程,一般把伺服器生成的dump檔案複製到本地或其他機器上進行分析。【記憶體分析】
    引數
    
    -J< flag >                 
    因為 jhat 命令實際上會啟動一個JVM來執行, 通過 -J 可以在啟動JVM時傳入一些啟動引數. 例如, -J-Xmx512m 則指定執行 jhat 的Java虛擬機器使用的最大堆記憶體為 512 MB. 如果需要使用多個JVM啟動引數,則傳入多個 -Jxxxxxx.
    -stack false|true 
    關閉物件分配呼叫棧跟蹤(tracking object allocation call stack)。 如果分配位置資訊在堆轉儲中不可用. 則必須將此標誌設定為 false. 預設值為 true.
    -refs false|true 
    關閉物件引用跟蹤(tracking of references to objects)。 預設值為 true. 預設情況下, 返回的指標是指向其他特定物件的物件,如反向連結或輸入引用(referrers or incoming references), 會統計/計算堆中的所有物件。
    -port port-number 
    設定 jhat HTTP server 的埠號. 預設值 7000-exclude exclude-file 
    指定物件查詢時需要排除的資料成員列表檔案(a file that lists data members that should be excluded from the reachable objects query)。 例如, 如果檔案列列出了 java.lang.String.value , 那麼當從某個特定物件 Object o 計算可達的物件列表時, 引用路徑涉及 java.lang.String.value 的都會被排除。
    -baseline exclude-file 
    指定一個基準堆轉儲(baseline heap dump)。 在兩個 heap dumps 中有相同 object ID 的物件會被標記為不是新的(marked as not being new). 其他物件被標記為新的(new). 在比較兩個不同的堆轉儲時很有用。
    -debug int 
    設定 debug 級別. 0 表示不輸出除錯資訊。 值越大則表示輸出更詳細的 debug 資訊。
    -version 
    啟動後只顯示版本資訊就退出。
  • JSTACK

  • JCONSOLE

效能調優

  1. 常用思路

    1. 優化sql

    2. 監控CPU

    3. 監控記憶體

      • FULL GC 垃圾收集時間過長

      • 解決方案

        • 調整堆記憶體大小

問題:

  1. 不定期出現記憶體溢位,把堆記憶體加大也沒用,匯出記憶體資訊沒有任何資訊.記憶體監控,也正常

處理思路:

  1. 控制變數法
    1. 硬體環境
      1. CPU
      2. 記憶體
    2. 軟體環境
      1. 作業系統
      2. Java版本
      3. 容器
      4. 程式碼問題