用Jcmd命令分析虛擬機器Metaspace元空間

Java老K發表於2020-10-26

使用JDK 11時jcmd新增了一個新的診斷命令:jcmd:VM.metaspace 虛擬機器元空間

此命令對於分析元空間消耗非常有用。因此,讓我們深入研究並使用它來重新訪問我們的小WildFly伺服器,它可以從以前的文章中獲得。我們描述了命令輸出和選項,以及如何使用它來發現典型的浪費點。

虛擬機器元空間,與JDK-8201572一起推出-由SAP和Red Hat提供-是jcmd的新增加。

與該集合中的其他診斷命令一樣,您將其命名為:jcmd<pid or process name> 虛擬機器元空間.

$ jcmd wildfly help VM.metaspace 
17680:
VM.metaspace
Prints the statistics for the Metaspace

Impact: Medium: Depends on number of classes loaded.

Permission: java.lang.management.ManagementPermission(monitor)

Syntax : VM.metaspace [options]

Options: (options must be specified using the <key> or <key>=<value> syntax)
        basic : [optional] Prints a basic summary (does not need a safepoint). (BOOLEAN, false)
        show-loaders : [optional] Shows usage by class loader. (BOOLEAN, false)
        show-classes : [optional] If show-loaders is set, shows loaded classes for each loader. (BOOLEAN, false)
        by-chunktype : [optional] Break down numbers by chunk type. (BOOLEAN, false)
        by-spacetype : [optional] Break down numbers by loader type. (BOOLEAN, false)
        vslist : [optional] Shows details about the underlying virtual space. (BOOLEAN, false)
        vsmap : [optional] Shows chunk composition of the underlying virtual spaces (BOOLEAN, false)
        scale : [optional] Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) or "dynamic" for a dynamically choosen scale. (STRING, dynamic)

VM.Metaspace基本情況

如果不使用引數,該命令將列印出一個簡短的標準統計資訊。

示例:同樣,我們啟動的WildFly 16.0.0獨立例項執行在sapmache11上,沒有執行的應用程式:

$ jcmd wildfly VM.metaspace
31997:

Total Usage ( 1041 loaders):

  Non-Class: 2837 chunks,     58,62 MB capacity,    53,54 MB ( 91%) used,     4,90 MB (  8%) free,     2,59 KB ( <1%) waste,   177,31 KB ( <1%) overhead, deallocated: 5065 blocks with 1,01 MB
      Class: 1653 chunks,      9,93 MB capacity,     7,44 MB ( 75%) used,     2,40 MB ( 24%) free,   208 bytes ( <1%) waste,   103,31 KB (  1%) overhead, deallocated: 653 blocks with 285,77 KB
       Both: 4490 chunks,     68,55 MB capacity,    60,98 MB ( 89%) used,     7,29 MB ( 11%) free,     2,79 KB ( <1%) waste,   280,62 KB ( <1%) overhead, deallocated: 5718 blocks with 1,29 MB

Virtual space:
  Non-class space:       60,00 MB reserved,      58,75 MB ( 98%) committed 
      Class space:      248,00 MB reserved,      10,00 MB (  4%) committed 
             Both:      308,00 MB reserved,      68,75 MB ( 22%) committed 

Chunk freelists:
   Non-Class:

 specialized chunks:    1, capacity 1,00 KB
       small chunks:   11, capacity 44,00 KB
      medium chunks: (none)
   humongous chunks: (none)
              Total:   12, capacity=45,00 KB
       Class:

 specialized chunks: (none)
       small chunks:    2, capacity 4,00 KB
      medium chunks: (none)
   humongous chunks: (none)
              Total:    2, capacity=4,00 KB

Waste (percentages refer to total committed size 68,75 MB):
              Committed unused:    156,00 KB ( <1%)
        Waste in chunks in use:      2,79 KB ( <1%)
         Free in chunks in use:      7,29 MB ( 11%)
     Overhead in chunks in use:    280,62 KB ( <1%)
                In free chunks:     49,00 KB ( <1%)
Deallocated from chunks in use:      1,29 MB (  2%) (5718 blocks)
                       -total-:      9,06 MB ( 13%)


MaxMetaspaceSize: 256,00 MB
InitialBootClassLoaderMetaspaceSize: 4,00 MB
UseCompressedClassPointers: true
CompressedClassSpaceSize: 248,00 MB

In-Use-Chunks 部分

第一部分向我們展示了有關活動類裝入器使用的塊的資訊:

Total Usage ( 1041 loaders):

  Non-Class: 2837 chunks,     58,62 MB capacity,    53,54 MB ( 91%) used,     4,90 MB (  8%) free,     2,59 KB ( <1%) waste,   177,31 KB ( <1%) overhead, deallocated: 5065 blocks with 1,01 MB
      Class: 1653 chunks,      9,93 MB capacity,     7,44 MB ( 75%) used,     2,40 MB ( 24%) free,   208 bytes ( <1%) waste,   103,31 KB (  1%) overhead, deallocated: 653 blocks with 285,77 KB
       Both: 4490 chunks,     68,55 MB capacity,    60,98 MB ( 89%) used,     7,29 MB ( 11%) free,     2,79 KB ( <1%) waste,   280,62 KB ( <1%) overhead, deallocated: 5718 blocks with 1,29 MB

總共有1041個裝載機還活著。

Non Class:,Class:-這些行列出了非類空間和類空間的塊使用情況。我們暫時忽略它們,改為檢視下一行:

Both:-總結了兩個空間的塊使用情況,因此也總結了整個VM的塊使用情況。這裡我們看到,我們的1041裝載機總共使用4490個塊,總“容量”為68.55MB。

容量是分配給類裝載機的所有空間的總和。記憶體繫結到一個類裝入器,但不一定是用於後設資料的空間,因為-還記得嗎?-我們為類載入器提供了超出其需要的數量。接下來的數字更能說明兩者的區別:

60,98 MB ( 89%) used,     7,29 MB ( 11%) free,     2,79 KB ( <1%) waste,   280,62 KB ( <1%) overhead

要理解這些數字,請記住類裝入器(ClassLoaderMetaspace)包含一個正在使用的塊的列表。它有一個用於滿足未來分配的當前塊,以及任何數量的(幾乎)完全使用的“失效”塊:

www.javakk.com

capacity = used + free + waste + overhead

在我們的WildFly示例中,類裝入器的68.55mb元空間(“容量”)中,實際使用(“used”)的只有60,98 MB(89%)。其餘分為:

  • free:當前塊中未使用的空間稱為“free”。如果這個載入器碰巧載入了更多的類,這個空間仍然可以使用。但是,如果裝載機完成裝載,這個空間將被浪費。
  • waste:非當前塊中未使用的空間稱為“waste”:噹噹前塊的大小不足以滿足記憶體請求時,分配一個新的塊,當前塊被“退役”。剩下的空間是浪費。然而,JVM在嘗試重用記憶體時會經歷一些困難,因此這個數字應該非常小。
  • overhead:塊有標題。這些標頭會產生一定的開銷。它通常很小。
    此外,我們還有:
deallocated: 5718 blocks with 1,29 MB

這是在解除安裝分配載入程式之前過早地返回給VM的元空間。這種情況很少發生。當一個類被重新定義並且其舊後設資料的一部分已經過時時,可能會發生這種情況。當VM在類載入過程中遇到問題並停止載入這個類,但已經為它的部分分配了元空間時,也可能發生這種情況。

虛擬機器試圖挽救那些被交易的區塊,但熱情有限。這是非常好的,因為這些案件應該是相當罕見的,他們造成的浪費小。

Virtual Space 部分

下一節列出虛擬機器用於元空間目的的虛擬空間總和:

Virtual space:
  Non-class space:       60,00 MB reserved,      58,75 MB ( 98%) committed
      Class space:      248,00 MB reserved,      10,00 MB (  4%) committed 
             Both:      308,00 MB reserved,      68,75 MB ( 22%) committed 

這很有趣,因為這是“真相”,也就是“作業系統所看到的”。reserved是作業系統為Metaspace保留的總記憶體,committed顯然是已提交的部分。這些數字包括類後設資料實際使用的空間和已累計的所有型別的浪費。

committed比在用塊的容量大是正常的,因為它還包含freelists中保留的空閒塊和HWM margin-space主動提交但還沒有被分割到metachunk中:

提交記憶體大小=正在使用的塊容量+空閒列表中的塊容量+HWM邊距。

更多細節可以參考這兩篇:

http://javakk.com/413.html

http://javakk.com/395.html

請看一下非類空間的提交大小如何與保留大小接近。這是因為在非類空間中,我們有一個記憶體對映列表,並根據需要新增到它們中,因此保留和提交之間的大小差不能大於一個區域的大小(通常為2MB)。而對於類空間,我們預先保留了整個空間(CompressedClassSize),這顯示在類空間的保留行中。

Chunk Freelist 部分

本節顯示空閒列表中有多少塊等待重用:

Chunk freelists:
   Non-Class:

 specialized chunks:    1, capacity 1,00 KB
       small chunks:   11, capacity 44,00 KB
      medium chunks: (none)
   humongous chunks: (none)
              Total:   12, capacity=45,00 KB
       Class:

 specialized chunks: (none)
       small chunks:    2, capacity 4,00 KB
      medium chunks: (none)
   humongous chunks: (none)
              Total:    2, capacity=4,00 KB

如果我們有高碎片(許多類裝入器並行活動,其中一部分已經死亡並被收集),那麼這可能是元空間的一個重要部分。在我們的例子中,它看起來完全無害,因為WildFly伺服器還沒有解除安裝任何類。

Waste 部分

可以說這是整個輸出中最有用的部分。

在開發虛擬機器元空間我們想一眼就能發現最常見的問題。因此,“浪費”部分列出了各種浪費點:

Waste (percentages refer to total committed size 68,75 MB):
              Committed unused:    156,00 KB ( <1%)
        Waste in chunks in use:      2,79 KB ( <1%)
         Free in chunks in use:      7,29 MB ( 11%)
     Overhead in chunks in use:    280,62 KB ( <1%)
                In free chunks:     49,00 KB ( <1%)
Deallocated from chunks in use:      1,29 MB (  2%) (5718 blocks)
                       -total-:      9,06 MB ( 13%)

我們通常只有兩個重要部分:

  • Free in chunks in use:這是已經分配給類載入器但仍然未使用的空間。請注意,嚴格地說,這不是“浪費”—理論上,載入程式可以繼續載入類,然後使用這個空間。但是如果沒有載入更多的類,這個記憶體確實是浪費了。
  • 看看這是如何對我們的這個浪費點總計7.29MB,約佔所承諾的元空間總大小的11%。
  • In free chunks:空閒列表中所有塊的總和。如上所述,當類載入器死亡並且存在大量碎片時,這種情況會增長。
  • Committed unused:已從當前VirtualSpaceNode提交的空間,但尚未分割成塊並分發給載入程式。通常應該很小。
  • Waste in chunks in use:使用中的塊中的“廢物”數量的總和。應該很小。
  • Overhead in chunks in use:塊使用部分中的“開銷”數的總和。應該很小。
  • Deallocated from chunks in use:使用中的塊部分中的“釋放”數的總和。應該很小。如果不是,這可能意味著大量的類重新定義或大量失敗的類載入。

A Pathological Case

到目前為止,這並不是很令人興奮,因為我們的WildFly伺服器像一隻行為良好的貓一樣發出平穩的呼嚕聲。就記憶體浪費而言,這裡沒什麼可看的。所以讓我們看看一個真正的病態病例:

InterleavedLoaders是一個小例子,它演示瞭如果死載入程式與Metaspace中的生命載入程式交錯,那麼即使在收集了類裝入器之後,VM如何儲存元空間記憶體。

它將建立許多類裝入器並使用它們來載入類。這些裝載機分為四組,或“幾代”,我們將解除安裝一代又一代,直到只剩下一代。由於它們是以交叉方式建立的,剩餘的生命載入程式將阻止死載入程式的空間返回到作業系統,因為,請記住:只有當整個VirtualSpaceNode(通常為2MB)空閒時,元空間記憶體才會釋放給作業系統。

讓我們啟動這個測試程式,並繼續按它的鍵,直到解除安裝四代裝載機中的三代:

$ java -cp ./repros/repros8/target/repros8-1.0.jar de.stuefe.repros.metaspace.InterleavedLoaders 
Generating 100 classes...
Will load 4 generations of 100 loaders each,  each loader loading 100 classes...
<press key>
After loading...
<press key>
Before freeing generation 1...
<press key>
After freeing generation 1.
<press key>
Before freeing generation 2...
<press key>
After freeing generation 2.
<press key>
Before freeing generation 3...
<press key>
After freeing generation 3.
<press key>

現在,讓我們看一下jcmd

$ jcmd  de.stuefe.repros.metaspace.InterleavedLoaders VM.metaspace
6918:

<cut>

Waste (percentages refer to total committed size 404,82 MB):
              Committed unused:    116,00 KB ( <1%)
        Waste in chunks in use:      2,95 KB ( <1%)
         Free in chunks in use:      6,41 MB (  2%)
     Overhead in chunks in use:    219,69 KB ( <1%)
                In free chunks:    275,21 MB ( 68%)
Deallocated from chunks in use:      1,29 MB ( <1%) (2227 blocks)
                       -total-:    283,24 MB ( 70%)

如果400MB、275MB(或幾乎70%)未使用並保留在免費列表中,我們可以看到承諾的大小。這清楚地顯示了元空間碎片是如何造成傷害的——作業系統會丟失這些記憶體,只要VM不重新載入類,它就會保持提交狀態,但不會使用。

為了確認,讓我們看看Freelist部分:

Chunk freelists:
   Non-Class:

 specialized chunks:    1, capacity 1,00 KB
       small chunks: 1147, capacity 4,48 MB
      medium chunks: 3844, capacity 240,25 MB
   humongous chunks: (none)
              Total: 4992, capacity=244,73 MB
       Class:

 specialized chunks: (none)
       small chunks: 1190, capacity 2,32 MB
      medium chunks:  901, capacity 28,16 MB
   humongous chunks: (none)
              Total: 2091, capacity=30,48 MB

所有的記憶體都在空閒列表中等待重用,但沒有返回到作業系統。

我目前正在開發一個原型,以減少元空間中的浪費和記憶體佔用,並更急切地將記憶體返回給作業系統。詳見JDK-8221173。

jcmd<pid>VM.metaspace 顯示基本元空間統計資訊

Virtual Space部分顯示用於所有元空間目的的保留和提交空間。總的來說,這就是元空間所使用的。它包括用於類後設資料的空間和開銷/浪費。

Waste部分列出了可能發生的所有型別的開銷/浪費。主要的浪費可能是:在使用中的free釋放塊-只被裝載機部分使用的塊-以及在free列表中以供重用的塊,以釋放的塊。

文章來源:http://javakk.com/417.html
也歡迎大家關注我的公眾號【Java老K】獲取更多幹貨

相關文章