我假设您可以在一段时间内使用相同的请求(请求集)创建问题。定义了MaxMetaspaceSize是一件好事,否则应用程序将使用本机内存,直到它耗尽增长。但我将从以下步骤开始:
- 检查在多次将同一请求发送到服务器时,在 JVM 中加载的类的数量是否持续增长。如果是,则可能正在创建动态类,这将导致在元空间中加载的类的增长。那么如何检查加载的类的数量,你可以使用visualvm通过JMX连接到服务器或在本地运行来模拟。我将提到本地的步骤,但对于远程连接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(it) + ”
"')
但是上面名为“类装入器装入类”的查询会很慢,这实际上将显示每个类装入器装入的类。
select { loader: cl,
classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
from instanceof java.lang.ClassLoader cl
- 然后尝试追踪元空间区域的增长。现在我们将使用jconsole和java具有的新东西:jmc(java任务控制)。您可以使用jconsole连接到jvm(本地或远程),一旦连接,就可以进入内存选项卡,您可以监视那里的非堆增长,其中应该有元空间和代码缓存以及压缩的类空间。现在连接
江铃汽车
连接到虚拟机,然后在连接后单击右上角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 根”,这将告诉您谁持有对类装入器的引用。