JVM--调优--04--案例07--元空间变大导致FULL GC

JVM–调优–04–案例07–元空间变大导致FULL GC


1、现象

Full GC (Metadata GC Threshold) //metaspace空间不够,导致Full GC 

Metaspace       used 35337K, capacity 56242K, committed 56320K, reserved 1099776K

  • used :已使用的空间大小
  • capacity:当前已经分配且未释放的空间容量大小
  • committed:当前已经分配的空间大小
  • reserved:预留的空间大小

1.1、元空间内存碎片化指标

capacity和used之差较大的时候,说明碎片化严重

1.2、内存碎片化严重的场景

  • 创建了较多的类加载器,同时使用率较低。

1.3、GC日志

{Heap before GC invocations=0 (full 0):
 par new generation   total 314560K, used 141123K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000)
  eden space 279616K,  50% used [0x00000000c0000000, 0x00000000c89d0d00, 0x00000000d1110000)
  from space 34944K,   0% used [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000)
  to   space 34944K,   0% used [0x00000000d3330000, 0x00000000d3330000, 0x00000000d5550000)
 concurrent mark-sweep generation total 699072K, used 0K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 35337K, capacity 56242K, committed 56320K, reserved 1099776K
  class space    used 4734K, capacity 8172K, committed 8172K, reserved 1048576K
1.448: [Full GC (Metadata GC Threshold) 1.448: [CMS: 0K->10221K(699072K), 0.0487207 secs] 141123K->10221K(1013632K), [Metaspace: 35337K->35337K(1099776K)], 0.0488547 secs] [Times: user=0.09 sys=0.00, real=0.05 secs] 
Heap after GC invocations=1 (full 1):
 par new generation   total 314560K, used 0K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000)
  eden space 279616K,   0% used [0x00000000c0000000, 0x00000000c0000000, 0x00000000d1110000)
  from space 34944K,   0% used [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000)
  to   space 34944K,   0% used [0x00000000d3330000, 0x00000000d3330000, 0x00000000d5550000)
 concurrent mark-sweep generation total 699072K, used 10221K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 35337K, capacity 56242K, committed 56320K, reserved 1099776K
  class space    used 4734K, capacity 8172K, committed 8172K, reserved 1048576K
}
{Heap before GC invocations=1 (full 1):
 par new generation   total 314560K, used 0K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000)
  eden space 279616K,   0% used [0x00000000c0000000, 0x00000000c0000000, 0x00000000d1110000)
  from space 34944K,   0% used [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000)
  to   space 34944K,   0% used [0x00000000d3330000, 0x00000000d3330000, 0x00000000d5550000)
 concurrent mark-sweep generation total 699072K, used 10221K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 35337K, capacity 56242K, committed 56320K, reserved 1099776K
  class space    used 4734K, capacity 8172K, committed 8172K, reserved 1048576K
1.497: [Full GC (Last ditch collection) 1.497: [CMS: 10221K->3565K(699072K), 0.0139783 secs] 10221K->3565K(1013632K), [Metaspace: 35337K->35337K(1099776K)], 0.0193983 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
Heap after GC invocations=2 (full 2):
 par new generation   total 314560K, used 0K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000)
  eden space 279616K,   0% used [0x00000000c0000000, 0x00000000c0000000, 0x00000000d1110000)
  from space 34944K,   0% used [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000)
  to   space 34944K,   0% used [0x00000000d3330000, 0x00000000d3330000, 0x00000000d5550000)
 concurrent mark-sweep generation total 699072K, used 3565K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 17065K, capacity 22618K, committed 35840K, reserved 1079296K
  class space    used 1624K, capacity 2552K, committed 8172K, reserved 1048576K
}

2、分析

当元空间出现内存碎片化时,就要考虑是否创建了大量的类加载器。

通过 dump 堆存储文件发现存在大量DelegatingClassLoader
通过进一步分析,发现是由于反射导致创建大量DelegatingClassLoader

2.1、反射创建大量 类加载器 的原理

使用反射调用方法的时候,有个调用次数阀值(默认15次)

  1. 反射调用方法的次数 < 15:通过JNI调用来实现方法的调用
  2. 反射调用方法的次数 >= 15:通过生成字节码来实现方法的调用,称为膨胀(inflation)机制。使用字节码的时候,会为该方法生成一个类加载器(DelegatingClassLoader)。如果存在大量方法经常反射调用,则会导致创建大量类加载器(DelegatingClassLoader)。

2.2、分析结论

反射调用导致创建大量DelegatingClassLoader,占用了较大的元空间内存,同时存在内存碎片化现象,导致元空间利用率不高,从而较快达到阈值,触发 FGC。

3、解决

  • 适当调大 metaspace 的空间大小。
  • 优化不合理的反射调用。例如最常见的属性拷贝工具类 BeanUtils.copyProperties 可以使用 mapstruct 替换。

4、日志解析

5.218: [Full GC (Metadata GC Threshold) TLAB: gc thread: 0x000055fa2f07c800 [id: 13] desired_size: 22370KB slow allocs: 0 refill waste: 357928B alloc: 1.00000 156625KB refills: 7 waste 13.2% gc: 21204128B slow: 13328B fast: 0B
TLAB totals: thrds: 1 refills: 7 max: 7 slow allocs: 0 max 0 waste: 13.2% gc: 21204128B max: 21204128B slow: 13328B max: 13328B fast: 0B max: 0B
5.218: [Tenured: 0K->15985K(2796224K), 0.1108898 secs] 156625K->15985K(4054528K), [Metaspace: 20986K->20986K(1067008K)], 0.1110571 secs] [Times: user=0.09 sys=0.02, real=0.11 secs]

  1. 5.218: 这是GC事件发生的时间戳。

  2. [Full GC (Metadata GC Threshold)]: 表示触发这次Full GC的原因是元数据GC阈值(Metadata GC Threshold)达到了。这通常是因为JVM的元空间(Metaspace)中的内存使用达到了一个特定的阈值,导致JVM需要进行Full GC来回收不再使用的元数据对象。

  3. TLAB: gc thread: 0x000055fa2f07c800 [id: 13]: 这部分信息描述了与TLAB相关的GC线程。每个线程在JVM中都有一个TLAB,用于快速分配对象。

  4. desired_size: 22370KB: 表示JVM期望的TLAB大小。

  5. slow allocs: 0: 表示没有因为TLAB空间不足而进行的慢分配,慢分配就是在堆上分配。

  6. refill waste: 357928B(349.53KB): 表示在重新填充TLAB时浪费的空间大小。

  7. alloc: 1.00000: 这可能是TLAB的分配比例或相关参数。

  8. 156625KB refills: 7 waste 13.2%: 这表示总共有7次TLAB重新填充,浪费了大约13.2%的空间。

  9. gc: 21204128B slow: 13328B fast: 0B: 这部分描述了由于TLAB操作导致的GC统计信息。gc 是通过TLAB触发的GC回收的总字节数,slow 是慢路径回收的字节数,fast 是快路径回收的字节数(在这里为0,表示没有通过快路径回收任何内存)。

  10. TLAB totals: 这部分总结了所有线程的TLAB统计信息。

  11. thrds: 1: 表示有一个线程参与了TLAB操作。

  12. refills, max, slow allocs, waste, gc, slow, fast: 这些是之前已经解释过的TLAB相关统计的汇总。

  13. [Tenured: 0K->15985K(2796224K), 0.1108898 secs]: 这部分描述了老年代(Tenured Generation)在GC前后的内存使用情况以及GC所花费的时间。

  14. 156625K->15985K(4054528K): 这是整个堆在GC前后的内存使用情况。

  15. [Metaspace: 20986K->20986K(1067008K)]: 这描述了元空间在GC前后的内存使用情况。

  16. [Times: user=0.09 sys=0.02, real=0.11 secs]: 这是GC操作的时间统计,包括用户模式CPU时间、系统模式CPU时间和实际经过的时间。

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM调优工具命令详解》是一份预习资料,主要介绍了Java虚拟机(JVM调优过程中使用的一些工具命令。这些工具命令可以帮助开发人员诊断和优化JVM的性能问题。 文中首先介绍了常用的JVM调优工具命令,包括jps、jstat、jinfo、jmap、jhat等。这些命令可以用于查看JVM进程信息、统计JVM内存和线程情况、获取JVM配置参数等。通过使用这些工具命令,开发人员可以快速定位JVM性能瓶颈所在,进行优化。 接下来,文中详细介绍了每个工具命令的使用方法和参数解释。例如,jstat命令可以用于查看JVM内存情况,包括堆内存使用量、垃圾回收情况等。而jmap命令可以用于生成堆内存转储文件,帮助开发人员分析内存泄漏问题。通过掌握这些工具命令的使用,开发人员可以更加高效地进行JVM调优。 此外,文中还介绍了一些实际的调优案例,通过使用这些工具命令来解决实际的JVM性能问题。这些案例包括内存泄漏、线程死锁、CPU占用过高等问题。通过学习这些案例,开发人员可以更好地理解如何利用工具命令来诊断和解决JVM性能问题。 总的来说,《JVM调优工具命令详解》是一份非常实用的预习资料,适合那些需要深入学习JVM性能优化的开发人员。通过学习和掌握这些工具命令,开发人员能够更加高效地进行JVM调优,提升应用程序的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值