1.问题背景
服务发布或者重启,JVM内存出现FullGC,导致发布过程中CPU负载升高,影响服务发布稳定
2.问题表现
以NN社区频道服务(服务名:nn-channel-business)为例,服务发布成功后,查询JVM 内存和状态
1.Grafana 查询JVM内存状态

结论:
堆内存未发生FullGC,但容器内Jstat查询 Garbage Collection 中 出现多次因为Metadata GC Threshold导致的FullGC,进而导致 JVM 出现 Pause
2.JVM 工具查询具体实例FGC次数
查询命令:jstat -gcutil 1

结论:
服务启动FGC次数为7,与步骤1中的Pause Duration对应
3.问题排查
关于MetaSpace的解释:
Metaspace 是 Java 8 及之后版本中用于取代永久代(PermGen)的内存区域,主要用于存储类的元数据信息,以下是关于它的详细解释:
作用
- 存储类元数据:负责存放类的结构信息,如类的名称、字段信息、方法信息、访问修饰符、常量池等。这些信息对于 JVM 在运行时加载、解析和执行类的代码至关重要。例如,当 JVM 需要创建一个类的实例时,它会首先在 Metaspace 中查找该类的元数据,以确定如何分配内存以及如何初始化实例。
- 支持动态类加载:在 Java 应用程序运行过程中,可能会动态地加载新的类,如通过使用类加载器加载插件、动态生成代理类等。Metaspace 为这些动态加载的类提供了存储和管理的空间,确保 JVM 能够正确地处理这些类的生命周期。
特点
- 与堆内存分离:Metaspace 与 Java 堆内存是相互独立的,它有自己的内存管理机制。这使得 Metaspace 的大小可以不受 Java 堆大小的限制,在一定程度上避免了因类元数据过多而导致的 Java 堆内存溢出问题。
- 使用本地内存:Metaspace 默认使用本地内存(Native Memory)来存储类元数据,而不是像永久代那样使用 Java 堆内存。这样可以利用操作系统的虚拟内存管理机制,更灵活地分配和管理内存,并且在 64 位系统中可以支持非常大的元数据空间。
- 可动态扩展和收缩:Metaspace 的大小可以根据应用程序的实际需求动态地扩展或收缩。当应用程序加载更多的类时,Metaspace 会自动分配更多的内存;而当一些类不再被使用并被卸载时,Metaspace 会回收相应的内存空间。
相关参数
- -XX:MetaspaceSize:用于设置 Metaspace 的初始大小。当 Metaspace 使用的内存超过这个初始大小时,JVM 会触发垃圾回收,并根据需要动态地扩展 Metaspace 的大小。
- -XX:MaxMetaspaceSize:用于限制 Metaspace 可以使用的最大内存大小。如果 Metaspace 使用的内存达到这个上限,JVM 会抛出 OutOfMemoryError 异常。
Metaspace 的出现是为了解决永久代在内存管理上的一些局限性,提高了 JVM 对类元数据的管理效率和灵活性,使得 Java 应用程序在处理大量类和动态类加载时能够更加稳定和高效地运行。
4.解决方案
Metaspace默认值初始值调整为对应参数
JVM启动参数增加 -XX:MetaspaceSize=360m (峰值数据冗余20%)
线上验证
重启一台服务实例:执行JVM 命令jstate -gcutil 1 查询GC情况
结果:初始化启动 未发生FGC,优化有效
5.后期展望
TODOLIST
1.Metaspace优化,进一步调整Metaspace内存,调整无用的类加载,优化常量池内存占用等,最终优化整体内存占用
