美文网首页
用Jcmd命令分析虚拟机Metaspace元空间

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

作者: lucas_sofia | 来源:发表于2020-10-27 00:02 被阅读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)包含一个正在使用的块的列表。它有一个用于满足未来分配的当前块,以及任何数量的(几乎)完全使用的“失效”块:

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

    相关文章

      网友评论

          本文标题:用Jcmd命令分析虚拟机Metaspace元空间

          本文链接:https://www.haomeiwen.com/subject/zkhtvktx.html