4年前 (2020-09-16)  爪哇岛 |   抢沙发  1339 
文章评分 0 次,平均分 0.0

使用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)包含一个正在使用的块的列表。它有一个用于满足未来分配的当前块,以及任何数量的(几乎)完全使用的“失效”块:

用jcmd命令分析虚拟机metaspace元空间

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边距。

更多细节可以参考这两篇:

https://javakk.com/413.html

https://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部分列出了可能发生的所有类型的开销/浪费。主要的浪费可能是:在使用中的免费块-只被装载机部分使用的块-以及在免费列表中以供重用的块,以免费的块。

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/417.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册