我假设您可以在一段时间内使用相同的请求(请求集)创建问题。定义 MaxMetaspaceSize 是一件好事,否则应用程序将使用本机内存,直到它耗尽增长。但我将从以下步骤开始:
- 当您多次将同一请求发送到服务器时,检查您在 JVM 中加载的类的数量是否持续增长。如果是,您可能正在创建动态类,这会导致元空间中加载的类增长。那么如何查看加载的类的数量,你可以使用visualvm通过JMX连接到服务器或者在本地运行来模拟。我将提到 local 的步骤,但是对于远程附加 JMX ,您应该将以下内容添加到应用程序的 JVM 参数中并启动它并在端口 9999 上使用 -XX:+UnlockDiagnosticVMOptions 进行远程连接。
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -XX:+UnlockDiagnosticVMOptions
将 visualvm (jvisualvm) 连接到 JVM 后,单击监视器,然后查看加载的类数。在那里,您可以监视堆以及元空间。但我会添加其他工具来密切监视元空间。
- 此外,一旦您连接到 jvm,您可能想要获取堆快照并找出使用 OQL 加载的类。因此,在您进行堆转储之前,请停止对服务器的请求,这样您就不会捕获任何正在进行的请求/执行代码及其相关对象,但这不是必需的。因此,在多次运行同一组请求后,在 visualvm 内部,在“监视器”空间中,单击右上角的“堆转储”。然后打开/加载快照,您将看到 OQL 控制台的选项。并且你会在 permgen 分析下的右下面板看到一些预定义的 OQL 查询。运行名为“类加载器加载类直方图”的查询,我想这将给出每个类加载器加载的类的计数。你可以用它来找出哪个类加载器正在加载类。
select map(sort(map(heap.objects('java.lang.ClassLoader'), '{loader: it, count: it.classes.elementCount }'), 'lhs.count < rhs.count'), 'toHtml (它) + "
"')
但是上面名为“类加载器加载的类”的查询会很慢,它实际上会显示每个类加载器加载的类。
select { loader: cl,
classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
from instanceof java.lang.ClassLoader cl
- 然后尝试追踪元空间区域的增长。现在我们将使用 jconsole 和 java 有的新东西:jmc(java 任务控制)。您可以使用 jconsole 连接到 jvm(本地或远程),一旦连接到内存选项卡,您就可以在那里监控非堆增长,它应该有元空间和代码缓存以及压缩的类空间。现在连接
江铃
连接到 VM,然后在连接后单击右上角 JMC 中的“诊断命令”。由于我们启用了 UnlockDiagnosticVMOptions ,因此可以执行 GC.class_stats 。您可能希望使用显示所有列并在 csv 中打印来运行它。所以命令看起来像:
GC.class_stats -all=true -csv=true
然后你可以比较不同时期的类统计数据,找出哪些类引起了麻烦(元空间增长)或哪些类在元空间中有相关信息(方法/方法数据)。如何分析当时收集的 csv 输出:好吧,我会将该 csv 加载到数据库或其他地方的两个相似表(代表 csv)中,以比较 GC.class_stats csv 输出,我可以在其中运行一些 SQL 或任何其他分析工具。这将更好地了解元空间中究竟在增长什么。GC 类统计信息具有以下列:
Index,Super,InstSize,InstCount,InstBytes,Mirror,KlassBytes,K_secondary_supers,VTab,ITab,OopMap,IK_methods,IK_method_ordering,IK_default_methods,IK_default_vtable_indices,IK_local_interfaces,IK_transitive_interfaces,IK_fields,IK_inner_classes,IK_signers,class_annotations,class_type_annotations,fields_annotations,fields_type_annotations,methods_annotations, methods_parameter_annotations,methods_type_annotations,methods_default_annotations,annotations,Cp,CpTags,CpCache,CpOperands,CpRefMap,CpAll,MethodCount,MethodBytes,ConstMethod,MethodData,StackMap,Bytecodes,MethodAll,ROAll,RWAll,Total,ClassName,ClassLoader
希望能帮助到你。此外,如果在 1.7 中没有导致任何泄漏,则该错误可能出现在 Java 8 中。
此外,如果任何人持有对类加载器的任何引用,这些类也不会从元空间中卸载。如果您知道您的类加载器应该是 GCed 并且没有人应该持有对您的类加载器的引用,您可以返回到 visualvm 中的堆转储并单击类加载器实例并右键单击以找到“最近的 GC 根”这将告诉你持有对类加载器的引用。