2

我们有一种情况,springboot 微服务的元空间不断增长,但堆表现良好。

jmap -clstats 显示有成千上万的以下类型的死类加载器。

0x00000000e3c8e3c8 1 4087 0x00000000e0004d18 死 com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl$TransletClassLoader@0x000000010087c7e8

在最初的高水位 GC 被触发,我看到元空间下降。在由于定义的元空间大小而触发的强制 GC 之后,我看到元空间不断增长,并且我看到更多相同类型的死类加载器被保留在元空间中。我确实看到了一些 GC 活动,但元空间消耗没有下降。但是,如果我通过 visualvm 强制 GC 收集,则会卸载大量类,并且元空间消耗将回到服务启动时的状态。

为什么 JVM 管理的 GC 不会卸载这些死掉的类加载器,而强制 GC 会呢?如果弱/软/幻像引用是原因,那么它不应该也适用于强制 GC 吗?

这是在Java8上。任何人都可以就我接下来应该看的地方给出一些指示吗?显然存在泄漏,那么有没有办法知道 TemplatesImpl$TransletClassLoader 的父类加载器?

感谢任何帮助。

4

1 回答 1

0

首先,JVM 只会在 Full GC 期间清除 Metaspace,而不会在 Young GC 期间清除。因此,您看到的行为是预期的。

在最初的高水位 GC 被触发,我看到元空间下降。

如果您检查 GC 跟踪,您将看到 System.GC() 调用。那是完整的GC。

但是,如果我通过 visualvm 强制 GC 收集,则会卸载大量类,并且元空间消耗将回到服务启动时的状态。

同样,这将是由 visualvm 触发的完整 GC,并且可以在 GC 跟踪中看到。这就是您看到利用率下降的原因。

根据您提供的描述,我认为您没有类加载器泄漏,因为在 Full GC 期间一切都得到了清理。根据我的经验,如果应用程序不必要地保持某些对象处于活动状态,因此即使在 Full GC 期间也不会收集垃圾,我只会将其视为“内存泄漏”。在您的情况下,我建议通过使用-XX:MaxMetaspaceSize标志来限制元空间大小。当占用率达到该阈值时,JVM 将自动触发 Full GC,正如您所注意到的,元空间使用率将下降。明智地设置该限制,因为太低的值会导致java.lang.OutOfMemoryError: Metaspace问题。

可以在此处找到有关 Metaspace 的更多详细信息。

于 2020-11-21T14:34:34.130 回答