使用JDK 11時(shí)jcmd添加了一個(gè)新的診斷命令:jcmd:VM.metaspace 虛擬機(jī)元空間
此命令對(duì)于分析元空間消耗非常有用。因此,讓我們深入研究并使用它來重新訪問我們的小WildFly服務(wù)器,它可以從以前的文章中獲得。我們描述了命令輸出和選項(xiàng),以及如何使用它來發(fā)現(xiàn)典型的浪費(fèi)點(diǎn)。
虛擬機(jī)元空間,與JDK-8201572一起推出-由SAP和Red Hat提供-是jcmd的新增加。
與該集合中的其他診斷命令一樣,您將其命名為:jcmd<pid or process name> 虛擬機(jī)元空間.
$ 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基本情況
如果不使用參數(shù),該命令將打印出一個(gè)簡(jiǎn)短的標(biāo)準(zhǔn)統(tǒng)計(jì)信息。
示例:同樣,我們啟動(dòng)的WildFly 16.0.0獨(dú)立實(shí)例運(yùn)行在sapmache11上,沒有運(yùn)行的應(yīng)用程序:
$ 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 部分
第一部分向我們展示了有關(guān)活動(dòng)類裝入器使用的塊的信息:
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個(gè)裝載機(jī)還活著。
Non Class:,Class:-這些行列出了非類空間和類空間的塊使用情況。我們暫時(shí)忽略它們,改為查看下一行:
Both:-總結(jié)了兩個(gè)空間的塊使用情況,因此也總結(jié)了整個(gè)VM的塊使用情況。這里我們看到,我們的1041裝載機(jī)總共使用4490個(gè)塊,總“容量”為68.55MB。
容量是分配給類裝載機(jī)的所有空間的總和。內(nèi)存綁定到一個(gè)類裝入器,但不一定是用于元數(shù)據(jù)的空間,因?yàn)?還記得嗎?-我們?yōu)轭惣虞d器提供了超出其需要的數(shù)量。接下來的數(shù)字更能說明兩者的區(qū)別:
60,98 MB ( 89%) used, 7,29 MB ( 11%) free, 2,79 KB ( <1%) waste, 280,62 KB ( <1%) overhead
要理解這些數(shù)字,請(qǐng)記住類裝入器(ClassLoaderMetaspace)包含一個(gè)正在使用的塊的列表。它有一個(gè)用于滿足未來分配的當(dāng)前塊,以及任何數(shù)量的(幾乎)完全使用的“失效”塊:
capacity = used + free + waste + overhead
在我們的WildFly示例中,類裝入器的68.55mb元空間(“容量”)中,實(shí)際使用(“used”)的只有60,98 MB(89%)。其余分為:
- free:當(dāng)前塊中未使用的空間稱為“free”。如果這個(gè)加載器碰巧加載了更多的類,這個(gè)空間仍然可以使用。但是,如果裝載機(jī)完成裝載,這個(gè)空間將被浪費(fèi)。
- waste:非當(dāng)前塊中未使用的空間稱為“waste”:當(dāng)當(dāng)前塊的大小不足以滿足內(nèi)存請(qǐng)求時(shí),分配一個(gè)新的塊,當(dāng)前塊被“退役”。剩下的空間是浪費(fèi)。然而,JVM在嘗試重用內(nèi)存時(shí)會(huì)經(jīng)歷一些困難,因此這個(gè)數(shù)字應(yīng)該非常小。
-
overhead:塊有標(biāo)題。這些標(biāo)頭會(huì)產(chǎn)生一定的開銷。它通常很小。
此外,我們還有:
deallocated: 5718 blocks with 1,29 MB
這是在卸載分配加載程序之前過早地返回給VM的元空間。這種情況很少發(fā)生。當(dāng)一個(gè)類被重新定義并且其舊元數(shù)據(jù)的一部分已經(jīng)過時(shí)時(shí),可能會(huì)發(fā)生這種情況。當(dāng)VM在類加載過程中遇到問題并停止加載這個(gè)類,但已經(jīng)為它的部分分配了元空間時(shí),也可能發(fā)生這種情況。
虛擬機(jī)試圖挽救那些被交易的區(qū)塊,但熱情有限。這是非常好的,因?yàn)檫@些案件應(yīng)該是相當(dāng)罕見的,他們?cè)斐傻睦速M(fèi)小。
Virtual Space 部分
下一節(jié)列出虛擬機(jī)用于元空間目的的虛擬空間總和:
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
這很有趣,因?yàn)檫@是“真相”,也就是“操作系統(tǒng)所看到的”。reserved是操作系統(tǒng)為Metaspace保留的總內(nèi)存,committed顯然是已提交的部分。這些數(shù)字包括類元數(shù)據(jù)實(shí)際使用的空間和已累計(jì)的所有類型的浪費(fèi)。
committed比在用塊的容量大是正常的,因?yàn)樗€包含freelists中保留的空閑塊和HWM margin-space主動(dòng)提交但還沒有被分割到metachunk中:
提交內(nèi)存大小=正在使用的塊容量+空閑列表中的塊容量+HWM邊距。
更多細(xì)節(jié)可以參考這兩篇:
請(qǐng)看一下非類空間的提交大小如何與保留大小接近。這是因?yàn)樵诜穷惪臻g中,我們有一個(gè)內(nèi)存映射列表,并根據(jù)需要添加到它們中,因此保留和提交之間的大小差不能大于一個(gè)區(qū)域的大?。ㄍǔ?MB)。而對(duì)于類空間,我們預(yù)先保留了整個(gè)空間(CompressedClassSize),這顯示在類空間的保留行中。
Chunk Freelist 部分
本節(jié)顯示空閑列表中有多少塊等待重用:
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
如果我們有高碎片(許多類裝入器并行活動(dòng),其中一部分已經(jīng)死亡并被收集),那么這可能是元空間的一個(gè)重要部分。在我們的例子中,它看起來完全無(wú)害,因?yàn)閃ildFly服務(wù)器還沒有卸載任何類。
Waste 部分
可以說這是整個(gè)輸出中最有用的部分。
在開發(fā)虛擬機(jī)元空間我們想一眼就能發(fā)現(xiàn)最常見的問題。因此,“浪費(fèi)”部分列出了各種浪費(fèi)點(diǎn):
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%)
我們通常只有兩個(gè)重要部分:
- Free in chunks in use:這是已經(jīng)分配給類加載器但仍然未使用的空間。請(qǐng)注意,嚴(yán)格地說,這不是“浪費(fèi)”—理論上,加載程序可以繼續(xù)加載類,然后使用這個(gè)空間。但是如果沒有加載更多的類,這個(gè)內(nèi)存確實(shí)是浪費(fèi)了。
- 看看這是如何對(duì)我們的這個(gè)浪費(fèi)點(diǎn)總計(jì)7.29MB,約占所承諾的元空間總大小的11%。
- In free chunks:空閑列表中所有塊的總和。如上所述,當(dāng)類加載器死亡并且存在大量碎片時(shí),這種情況會(huì)增長(zhǎng)。
-
Committed unused:已從當(dāng)前
VirtualSpaceNode提交的空間,但尚未分割成塊并分發(fā)給加載程序。通常應(yīng)該很小。 - Waste in chunks in use:使用中的塊中的“廢物”數(shù)量的總和。應(yīng)該很小。
- Overhead in chunks in use:塊使用部分中的“開銷”數(shù)的總和。應(yīng)該很小。
- Deallocated from chunks in use:使用中的塊部分中的“釋放”數(shù)的總和。應(yīng)該很小。如果不是,這可能意味著大量的類重新定義或大量失敗的類加載。
A Pathological Case
到目前為止,這并不是很令人興奮,因?yàn)槲覀兊腤ildFly服務(wù)器像一只行為良好的貓一樣發(fā)出平穩(wěn)的呼嚕聲。就內(nèi)存浪費(fèi)而言,這里沒什么可看的。所以讓我們看看一個(gè)真正的病態(tài)病例:
InterleavedLoaders是一個(gè)小例子,它演示了如果死加載程序與Metaspace中的生命加載程序交錯(cuò),那么即使在收集了類裝入器之后,VM如何保存元空間內(nèi)存。
它將創(chuàng)建許多類裝入器并使用它們來加載類。這些裝載機(jī)分為四組,或“幾代”,我們將卸載一代又一代,直到只剩下一代。由于它們是以交叉方式創(chuàng)建的,剩余的生命加載程序?qū)⒆柚顾兰虞d程序的空間返回到操作系統(tǒng),因?yàn)椋?qǐng)記?。褐挥挟?dāng)整個(gè)VirtualSpaceNode(通常為2MB)空閑時(shí),元空間內(nèi)存才會(huì)釋放給操作系統(tǒng)。
讓我們啟動(dòng)這個(gè)測(cè)試程序,并繼續(xù)按它的鍵,直到卸載四代裝載機(jī)中的三代:
$ 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>
現(xiàn)在,讓我們看一下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%)未使用并保留在免費(fèi)列表中,我們可以看到承諾的大小。這清楚地顯示了元空間碎片是如何造成傷害的——操作系統(tǒng)會(huì)丟失這些內(nèi)存,只要VM不重新加載類,它就會(huì)保持提交狀態(tài),但不會(huì)使用。
為了確認(rèn),讓我們看看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
所有的內(nèi)存都在空閑列表中等待重用,但沒有返回到操作系統(tǒng)。
我目前正在開發(fā)一個(gè)原型,以減少元空間中的浪費(fèi)和內(nèi)存占用,并更急切地將內(nèi)存返回給操作系統(tǒng)。詳見JDK-8221173。
jcmd<pid>VM.metaspace 顯示基本元空間統(tǒng)計(jì)信息
Virtual Space部分顯示用于所有元空間目的的保留和提交空間??偟膩碚f,這就是元空間所使用的。它包括用于類元數(shù)據(jù)的空間和開銷/浪費(fèi)。
Waste部分列出了可能發(fā)生的所有類型的開銷/浪費(fèi)。主要的浪費(fèi)可能是:在使用中的free釋放塊-只被裝載機(jī)部分使用的塊-以及在free列表中以供重用的塊,以釋放的塊。
文章來源:http://javakk.com/417.html
也歡迎大家關(guān)注我的公眾號(hào)【Java老K】獲取更多干貨