1. 程式人生 > >jvm性能監控與GC調優

jvm性能監控與GC調優

builder mat ont cati 顯示 %20 系統 處理 port

目錄

  • 一 提出問題
  • 二 基於JDK命令行工具的監控
    • 1. JVM的三種參數類型
      • 1.1 標準參數
      • 1.2 X 參數
      • 1.3 XX 參數
      • 1.4 常用命令
    • 2. jstat查看虛擬機統計信息
      • 2.1 類加載信息
      • 2.2 垃圾回收信息
      • 2.3 JIT編譯信息
    • 3. jmap + MAT分析內存溢出 [實戰]
      • 3.1 模擬內存溢出
      • 3.2 導出內存影像文件
      • 3.3 使用MAT分析dump文件
    • 4. jstack分析死循環與死鎖 [實戰]
  • 三 基於JVisualVM的可視化監控
  • 四 基於Btrace的監控調試
    • 4.1 簡介
    • 4.2 環境準備
    • 4.2 使用BTrace
      • 4.3.1 簡單使用
      • 4.3.2 攔截構造函數,重載方法
      • 4.3.3 攔截返回值,異常,行號
      • 4.3.4 攔截復雜參數,環境變量
      • 4.3.5 註意事項
  • 五 Tomcat性能監控與調優
  • 七 JVM層GC調優
  • 八 JVM字節碼與Java代碼層調優
  • 九 總結

一 提出問題

問題

  1. 生產環境發生了內存溢出該如何處理?
  2. 生產環境應該給服務器分配多少內存?
  3. 如何對垃圾收集器的性能進行調優?
  4. 生產環境CPU負載飆升如何處理?
  5. 生產環境應該給應用分配多少線程?
  6. 如何不加log就確定是否執行了某一行代碼?
  7. 如何不加log就能實時查看某個方法的參數值和返回值?
  8. JVM的字節碼
  9. 循環體中用"+"做字符串拼接為什麽效率低,“+”的底層一定是StringBuilder.append()嗎?
  10. String常量池
  11. i++與++i哪種寫法效率更高?
  12. jvm監控和調試工具的使用
  13. 理解JVM的GC機制,學會GC調優
  14. TomCat性能監控與調優

二 基於JDK命令行工具的監控

1. JVM的三種參數類型

1.1 標準參數

jvm的標準參數,在jvm各個版本中基本不變。
使用java獲取所有標準參數

  • -help
  • -server -client ^①^
  • -version -showversion
  • -cp -classpath

使用java -version命令可以輸出java的版本信息,從第4行可以看到:

JVM的名字(HotSpot)、類型(Server)、build ID(24.79-b02),JVM以混合模式

(mixed mode)在運行,這是HotSpot默認的運行模式,意味著JVM在運行時可以動態的把字節碼編譯為本地代碼。關於切換類型(Server Client)以及二者的區別詳見參考文檔1。

C:\Users\Administrator>java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

1.2 X 參數

非標準化參數,在jvm各個版本中會有一些小變化。
使用java -X獲取所有X參數

  • -Xint:JVM以解釋方式執行所有的字節碼,會顯著降低運行速度

  • -Xcomp:JVM在第一次使用時就把所有字節碼編譯為本地代碼

  • -Xmixed:混合模式(默認),由jvm自己決定是否編譯為本地代碼,會將字節碼中多次被調用的部分便以為本地代碼以提高執行效率;被調用很少的方法會在解釋模式下執行,減少編譯和優化成本

    # 默認是mixed mode(混合模式),在java的版本信息中顯示
    C:\Users\Administrator>java -version
    java version "1.8.0_51"
    Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
    Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)
    
    # 修改為interpreted mode(解釋執行)
    C:\Users\Administrator>java -Xint -version
    java version "1.8.0_51"
    Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
    Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, interpreted mode)
    
    # 修改為compiled mode(編譯執行)
    C:\Users\Administrator>java -Xcomp -version
    java version "1.8.0_51"
    Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
    Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, compiled mode

    測試三種JVM工作模式可以使用死循環代碼測試運行時間,具體命令如下:

    javac HelloWorld.java
    java -Xint HelloWorld
    java -Xcomp HelloWorld
    java -Xmixed HelloWorld

1.3 XX 參數

? 非標準化參數,在jvm各個版本中變化較大,主要用於jvm調優和debug,也是最常用的參數
? 使用java -XX:+PrintFlagsFinal -version > flags1.txt命令獲取所有XX參數^③^,大約有700+參數

  • Boolean 類型

    格式:-XX:[+-]

    舉例:-XX:UseConcMarkSweepGC 啟用CMS垃圾收集器
    ? -XX:+UseG1GC 啟用G1垃圾收集器

  • K-V 類型

    格式:-XX:[+-]

    舉例:-XX:InitialHeapSize=3116367872 初始化堆內存,常用-Xms512m表示,不要誤認為是X參數

    -XX:MaxHeapSize=3116367872 最大堆內存,常用-Xmx1024m表示
    -XX:ThreadStackSize=1024 線程堆棧大小,常用-Xss128k表示
    -XX:MaxGCPauseMills=500 表示GC的最大停頓時間是500ms
    -XX:GCTimeRatio=19

1.4 常用命令

  • jps:查看java進程。

    -m顯示傳遞給main方法的參數。

    -l顯示應用程序main類的完整包名稱或應用程序的JAR文件的完整路徑名。

    -v顯示傳遞給JVM的參數

  • jinfo:查看Java進程的修改過的jvm參數

    jinfo -flags 2345查看進程id為2345的jvm參數

    jinfo -flag MaxheapSize 2345查看進程id為2345的jvm參數最大堆內存MaxheapSize的值

2. jstat查看虛擬機統計信息

2.1 類加載信息

# 進程id21640 輸出間隔1000ms 輸出次數3
# Loaded加載類的數量  Bytes加載的kB數  Unloaded:卸載的類數  Bytes:卸載的Kbytes數Time:執行類加載和卸載操作所花費的時間
C:\Users\Administrator>jstat -class 21640 1000 3
Loaded  Bytes  Unloaded  Bytes     Time
  3010  5823.5       52    77.1       3.48
  3010  5823.5       52    77.1       3.48
  3010  5823.5       52    77.1       3.48

2.2 垃圾回收信息

# 輸出結果各項指標詳見參考文檔2
C:\Users\Administrator>jstat -gc 21640
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
18432.0 19456.0  0.0    0.0   77824.0  48459.9   126976.0   10715.1   19456.0 18288.3 2304.0 1968.8     13    0.204   1      0.139    0.342

2.3 JIT編譯信息

# 查看JIT編譯信息
C:\Users\Administrator>jstat -compiler 21640
Compiled Failed Invalid   Time   FailedType FailedMethod
    3054      1       0    12.35          1 org/apache/tomcat/util/IntrospectionUtils setProperty

3. jmap + MAT分析內存溢出 [實戰]

技術分享圖片

3.1 模擬內存溢出

/**
 * Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit 
 * exceeded
 */
private static void heapSize2() {
    Map<Object,Object> map = new HashMap();
    Random r = new Random();
    Integer i = 0;
    while (true) {
        map.put(i++, "aaa");
    }
}

/**
 * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 */
private static void heapSize() {
    List<String> names = new ArrayList();
    for (;;) {
        names.add("bbb");
    }
}

3.2 導出內存影像文件

  1. 使用命令行參數,內存溢出時自動導出(最常用)

    -XX+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./

    另外也可以使用-Xrunhprof:head=site 參數生成java.hprof.txt 文件,不過這樣會影響 JVM的運行效率,不建議在生產環境中使用

  2. 使用jmap命令手動導出 jmap -dump:format=b,file=heap.hprof 2345

format表示以二進制形式導出,file表示文件名稱,2345表示要操作的java進程id

更多jmap命令選項詳見參考文檔2

  1. 使用JConsole生成Dump文件技術分享圖片

3.3 使用MAT分析dump文件

首先下載MAT (Memory Analyzer),參考MAT教程^④⑤^,分析dump文件

技術分享圖片

Shallow Heap表示對象本身所占的內存^④⑤^,Retained Heap表示對象和對象中的引用總共占的內存大小。比如User對象包含name屬性,會引用String對象。

排除虛引用,根據GC Roots查看對象引用技術分享圖片

技術分享圖片

4. jstack分析死循環與死鎖 [實戰]

CPU負載(load average)^⑦^和使用率過高,很可能的原因就是死循環。

  1. 首先可以使用top命令查看占用CPU最高的進程信息,獲取進程pid

  2. 使用jstack [pid] > myStack.txt命令打印指定java進程的所有線程堆棧信息

  3. 使用Linux命令top -p [pid] -H監控指定進程中所有線程信息,然後找到CPU占用率高的線程nid(windows中可以process exlporer工具查看指定進程中所有線程信息

    技術分享圖片

    如圖可以看到8247(0x2037)線程的CPU使用率達到了96.7%,此時我們就可以確認是這個線程的問題了

  4. 在myStack.txt文件中查看線程id為nid的堆棧信息(myStack.txt線程id是16進制,top -p命令線程id是10進制),就可以找到導致CPU飆升的線程的堆棧信息了,然後分析問題,解決問題。技術分享圖片

    如圖可以看到8247(0x2037)線程的堆棧信息

  5. 另外jstack命令能自動檢測到死鎖,在myStack.txt末尾會有 “found 1 deadlock”,也會打印死鎖線程的相關信息,根據這些信息就可以分析問題,解決問題了。另外,也可以使用JConsole來檢測死鎖

技術分享圖片

三 基於JVisualVM的可視化監控

四 基於Btrace的監控調試

4.1 簡介

BTrace可以動態地向目標應用程序的字節碼註入追蹤代碼,用到的技術JavaCompilerAPI,JVMTI,Agent,Instrumentation+ASM

1、接口性能變慢,分析每個方法的耗時情況;

2、當在Map中插入大量數據,分析其擴容情況;

3、分析哪個方法調用了System.gc(),調用棧如何;

4、執行某個方法拋出異常時,分析運行時參數;

4.2 環境準備

下載JVisualVM BTrace插件https://visualvm.github.io/pluginscenters.html

下載BTracehttps://github.com/btraceio/btrace/releases/tag/v1.3.11

jar包

4.2 使用BTrace

4.3.1 簡單使用

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

@BTrace
public class PrintArgSimple {
    // 在Ch4Controller類的fun1方法的入口處(ENTRY)進行追蹤
    @OnMethod(clazz = "com.imooc.monitor_tuning.chapter4.Ch4Controller", 
            method = "fun1", location = @Location(Kind.ENTRY))
    public static void anyRead(@ProbeClassName String className, @ProbeMethodName String methodName, AnyType[] args) {
        BTraceUtils.printArray(args);  //fun1方法的所有參數
        BTraceUtils.println(className + ":" + methodName);
    }
}

btrace [pid] MyBtrace.java

4.3.2 攔截構造函數,重載方法

4.3.3 攔截返回值,異常,行號

4.3.4 攔截復雜參數,環境變量

4.3.5 註意事項

五 Tomcat性能監控與調優

七 JVM層GC調優

八 JVM字節碼與Java代碼層調優

九 總結

參考文檔:

  1. 關於JVM的類型和模式
  2. Java命令行工具幫助文檔 - Oracle
  3. 打印所有XX參數
  4. 生成dump文件與MAT的使用
  5. MAT使用教程
  6. MAT中文文檔
  7. 理解Linux系統負荷 - 阮一峰
  8. JVisual VM使用教程
  9. 如何在生產環境使用Btrace進行調試 - 占小狼

jvm性能監控與GC調優