1. 程式人生 > >如何監控 Tomcat 的記憶體佔用情況

如何監控 Tomcat 的記憶體佔用情況

目錄

Tomcat 是執行在 JVM(Java Virtual Machine) 中的一個 Java 程序, 它在執行過程中對記憶體的佔用情況, 可以藉助一些 JDK 的工具進行監控, 為優化提供資料支撐.

1 JVM 記憶體模型中的區域

1.1 執行緒棧區

壓入執行緒棧的每個棧幀(Stack Frame)中, 包含了程式指令以及區域性變量表, 每個方法呼叫對應一個棧幀.

程式指令包括程式計數器(PCR), 記錄程式執行到的位置, 便於方法呼叫或多執行緒切換結束後的工作恢復.

區域性變量表包括各種基本資料型別: boolean、byte、char、short、int、float、long、double以及物件的引用.

注意: 每個執行緒都有獨立的棧, 稱之為執行緒棧, 它們是互相隔離的.

1.2 Java Heap 區

Java Heap是被所有執行緒共享的一塊記憶體區域, 在虛擬機器啟動時建立. 此記憶體區域的唯一目的就是存放物件例項, 幾乎所有的物件例項都在這裡分配記憶體.

1.3 靜態方法區

又稱為永久代(Perm Generation), 用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料.

對於永久代的常見配置有: -XX:PermSize=256m -XX:MaxPermSize=512m.

說明: JDK 8.0中用Meta Space(元空間)替代Perm Generation, 因此在配置 JVM 啟動引數的時候, 需要作如下配置:

-XX:MetaspaceSize=256m     # 對比JDK 7.0的 PermSize
-XX:MaxMetaspaceSize=512m  # 對比JDK 7.0的 MaxPermSize

1.4 JDK 8.0中的元空間

JDK 8.0 的HotSpot 實現中, 使用本地記憶體來儲存類的元資料, 這個區域被稱為元空間.

  • 元空間有如下特點:

    1. 充分利用了Java語言規範中的好處: 類及相關元資料的生命週期與類載入器的生命週期一致;
    2. 每個類載入器都有各自專用的儲存空間;
    3. 元空間只進行線性分配;
    4. 不會單獨回收某個類;
    5. 省掉了GC掃描及壓縮的時間;
    6. 元空間中物件的位置是固定的;
    7. 如果GC發現某個類載入器不再存活, 它就會把與這個類載入器相關的空間全部回收.
  • 元空間的記憶體分配模型:

    1. 絕大多數類的元資料空間都從本地記憶體中分配;
    2. 用來描述類的元資料的類也被刪除了;
    3. 分元資料分配了多個虛擬記憶體空間;
    4. 給每個類載入器分配一個記憶體塊的列表, 塊的大小取決於類載入器的型別; sun/反射/代理對應的類載入器的塊會小一些;
    5. 歸還記憶體塊, 釋放記憶體塊列表;
    6. 一旦元空間的資料被清空了, 虛擬記憶體的空間就會被回收;
    7. 減少碎片的策略.

2 JDK 工具的使用

JDK自帶的工具位於${JAVA_HOME}/bin/目錄下.

JConsole 可以簡單明瞭地檢視到記憶體的使用情況, 執行緒的狀態, 當前載入的類的總量等.

JVisualVM 可以下載外掛(如GC等), 進而檢視更豐富的資訊. 如果是分析本地的Tomcat的話, 還可以進行記憶體抽樣等, 檢查每個類的使用情況.

  • jps 檢視本地執行著的 Java 程序, 及其程序號、程序啟動的路徑等資訊;

  • jmap 檢視垃圾收集策略即 JVM 記憶體佔用情況:

    jmap -heap pid # 檢視垃圾收集策略, 以及堆記憶體的分配、使用情況.

    jmap -clstats pid # 檢視類載入器的統計資料 --- 此命令呼叫了sun.jvm.hotspot.runtime.VM.initialize() 方法, 會導致該 pid 對應的 JVM 程序阻塞.

    jmap -histo [pid] # 按照記憶體使用大小倒序列出記憶體中的例項型別.

  • jstack 檢視執行緒棧:

    jstack pid # 列出該 pid 對應 JVM 的所有執行緒棧描述, 主要包括每個執行緒的狀態以及堆疊內各棧幀的方法全限定名、程式碼位置. 注意: 這些資訊的顯示只是為了便於開發人員閱讀, 並不是棧中存的就是這些資訊.

  • jstat 實時檢視堆記憶體的使用情況:

    # 使用方法: 
    jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
    # 檢視可使用的選項: 
    jstat –options 
    -class            # 類載入情況的統計
    -compiler         # HotSpot中即時編譯器編譯情況的統計
    -gc
    -gccapacity       # 新生代、老年代以及永久代的儲存容量情況
    -gccause
    -gcmetacapacity   # 元資料區的容量
    -gcnew            # 新生代垃圾回收資訊
    -gcnewcapacity    # 新生代的儲存容量
    -gcold            # 老年代垃圾回收資訊
    -gcoldcapacity    # 老年代的儲存容量
    -gcutil           # 實時檢視GC資訊
    -printcompilation # HotSpot編譯方法的統計
    • 使用示例: 間隔5s, 每隔10條輸出一次頭資訊, 列印程序號為3308的JVM程序的堆記憶體使用情況, 以及各代垃圾回收的次數及時間: jstat -gcutil -h10 77545 5000

    • 顯示資訊如下: !圖02.png

    • 引數說明:
    S0: Heap上的Survivor Space 0區已使用空間的百分比
    S1: Heap上的Survivor Space 1區已使用空間的百分比
    E:  Heap上的Eden Space區已使用空間的百分比
    O:  Heap上的Old Space區已使用空間的百分比
    M:  Meta Space(元資料區)已使用空間的百分比
    YGC: 從應用程式啟動到取樣時發生Young GC的次數
    YGCT: 從應用程式啟動到取樣時Young GC所用的時間(單位: 秒)
    FGC:  從應用程式啟動到取樣時發生Full GC的次數
    FGCT: 從應用程式啟動到取樣時Full GC所用的時間(單位: 秒)
    GCT:  從應用程式啟動到取樣時用於垃圾回收的總時間(單位: 秒)

3 檢視 GC 日誌資訊

可以通過配置JVM的啟動引數, 列印類的載入情況及物件的回收資訊, 可以列印到螢幕或指定檔案中, 預設也會列印到catalina.log中. Tomcat容器的JVM啟動引數配置檔案是: ${TOMCAT_HOME}/bin/catalina.sh, 具體引數如下:

-verbose:gc          # 在輸出裝置顯示垃圾收集資訊(JVM發生記憶體回收時輸出相關資訊)
-XX:+PrintGC         # 輸出GC日誌, 形式: Full GC 118250K->113543K(130112K), 0.0094143 secs 
-XX:+PrintGCDetails  # 輸出GC詳細日誌, 形式: GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs[Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs] 

-XX:+PrintGCTimeStamps  # 輸出GC的時間戳, 以基準時間的形式輸出: 11.851: [GC 98328K->93620K(130112K), 0.0082960 secs], 11.851是JVM啟動後的秒數. 
-XX:+PrintGCDateStamps  # 輸出GC的時間戳, 以日期的形式輸出: 2018-08-28T21:53:59.234+0800. 

-XX:+PrintGCApplicationStoppedTime     # 列印垃圾回收期間程式暫停的時間, 即GC消耗的時間. 可與上面混合使用. 輸出形式: Total time for which application threads were stopped: 0.0468229 seconds 
-XX:+PrintGCApplicationConcurrentTime  # 列印每次垃圾回收前, 程式未中斷的執行時間, 即相鄰2次GC的間隔. 可與上面混合使用. 輸出形式: Application time: 0.5291524 seconds 

-XX:+PrintTenuringDistribution   # 觀察各個Age的物件總大小 
-XX:PrintHeapAtGC                # 列印GC前後的詳細堆疊資訊. 
-XX:+HeapDumpOnOutOfMemoryError  # 發生OOM時自動dump堆疊資訊, 以便後續分析. 

-Xloggc:../logs/gc.log     # 與上面選項配合使用, 將日誌資訊輸出到指定的檔案以便後續分析. 

4 新增 JMS 遠端監控

對部署在區域網內其他伺服器上的Tomcat, 可以開啟JMX監控埠, 就可以在另外的伺服器上通過該埠檢視常用的引數(一些比較複雜的功能不支援).

配置方法: 同樣是在JVM啟動引數中配置, 配置如下:

-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=172.16.11.62   # 設定JVM的JMS監聽的IP地址, 防止錯誤監聽為本機127.0.0.1地址
-Dcom.sun.management.jmxremote.port=1090  # 設定JVM的JMS監控的埠
-Dcom.sun.management.jmxremote.ssl=false  # 設定JVM的JMS監控不實用SSL
-Dcom.sun.management.jmxremote.authenticate=false  # 設定JVM的JMS監控不需要認證