4年前 (2020-09-27)  Java系列 |   抢沙发  7950 
文章评分 1 次,平均分 1.0

这个java.lang.OutOfMemoryError:Metaspace表示为Java类元数据分配的本机内存量已被耗尽。让我们来看看如何解决这个问题。

一般来说,可以在命令行上设置MaxMetaSpaceSize

java -XX:MaxMetaspaceSize=3200m

你可以试着增加它的价值,看看它是否能解决问题。还要记住,减小Java堆的大小将为MetaSpace提供更多的可用空间。Java堆和元空间之间存在一种折衷,因此您必须找到一种平衡。

如果问题仍然存在,那么有必要通过检查JVM中加载的类的数量来分析到底发生了什么。为此,可以使用类似VisualVM的外观。

请注意,JVisualVM从oraclejdk9开始就已经停止了。您需要从这里下载:http://visualvm.github.io/download.html

将visualvm(jvisualvm)连接到JVM之后,单击monitor,然后查看加载的类的数量。在那里您可以监视堆和元空间。如果您可以看到类的数量在请求之间增加,那么您很可能正在动态创建新类。

解决OutOfMemoryError:metaspace元空间溢出问题总结

可以通过堆转储执行进一步的检查:

解决OutOfMemoryError:metaspace元空间溢出问题总结

然后,如果您查看了OQL控制台,就可以执行OQL查询来对您的类执行即席分析。例如,通过执行以下查询,可以从每个类加载器加载一个类列表:

select map(sort(map(heap.objects('java.lang.ClassLoader'), '{loader: it, count: it.classes.elementCount }'), 'lhs.count < rhs.count'), 'toHtml(it) + "
"')

解决OutOfMemoryError:metaspace元空间溢出问题总结

这是一个宝贵的提示,可以用来确定类加载器是否正在加载越来越多的类。

关于metaspace内存溢出排查细节也可以参考之前的文章:https://javakk.com/160.html

我假设您可以在一段时间内使用相同的请求(一组请求)创建问题。定义了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之后,单击monitor,然后查看加载的类的数量。在那里您可以监视堆和元空间。但是我将添加其他工具来密切监视元空间。

另外,一旦连接到jvm,您可能需要获取堆快照并找出使用OQL加载的类。因此,在进行堆转储之前,请停止对服务器的请求,这样就不会捕获任何正在执行的请求/正在执行的代码及其相关对象,但这不是必需的。因此,在visualvm中的“monitor”空间中多次运行同一组请求后,单击右上角的“Heap Dump”。然后打开/加载快照,您将看到OQL控制台的选项。在permgen analysis下的右下面板上会看到一些预定义的OQL查询。运行名为“classloaded class histogram”的查询,我想这将给出每个类加载器加载的类的计数。您可以使用它来找出哪个类加载器正在加载类。

但是上面名为“classloader loaded class”的查询将很慢,这将实际显示每个类加载器加载的类。

select { loader: cl,
             classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
    from instanceof java.lang.ClassLoader cl

然后试着追踪元空间区域的生长情况。现在我们将使用jconsole和java的一些新功能:jmc(java任务控制)。您可以使用jconsole连接到jvm(本地或远程),一旦连接到jvm,进入memory选项卡,您就可以监视那里的非堆增长,那里应该有元空间、代码缓存和压缩的类空间。现在连接

jmc

要连接到虚拟机,然后在连接之后,单击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_superss,VTab,ITab,OopMap,IK_methods,IK_default_methods,IK_default_vtable_索引,IK_local_接口,IK_可传递的_接口,IK_字段,IK_内部_类,IK_signers,class_注解,class_type_注释,字段,方法\注释、方法\参数\注释、方法\类型\注释、方法\默认注释、注释、Cp、CpTags、CpCache、CpOperands、CpRefMap、CpAll、MethodCount、MethodBytes、ConstMethod、MethodData、StackMap、字节码、MethodAll、ROAll、RWAll、Total、ClassName、ClassLoader

希望有帮助。另外,如果1.7中没有导致任何漏洞,那么这个bug可能在Java8中。

此外,如果有人持有对类加载器的任何引用,则不会从元空间卸载类。如果您知道您的类加载器应该是GCed,并且没有人应该持有对您的类加载器的引用,那么您可以返回到visualvm中的heap dump,单击class loader instance并右键单击找到“nearest GC root”,它将告诉您谁持有对类加载器的引用。

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/485.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册