用Jcmd命令分析虚拟机Metaspace元空间

2020-10-27  本文已影响0人  lucas_sofia

使用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%)。其余分为:

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%)

我们通常只有两个重要部分:

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】获取更多干货

上一篇下一篇

猜你喜欢

热点阅读