java8“java.lang.OutOfMemoryError:Metaspace”

时间:2016-03-17 04:39:44

标签: java out-of-memory metaspace

将我们的Java应用程序(在Tomcat上运行的服务)JRE从Java 7切换到Java 8后,我们在运行了几天高流量后开始看{4}}。

堆使用情况还可以。在性能测试期间执行相同的代码流之后,Metaspace会跳转。

可能导致元空间内存问题的原因是什么?

当前设置为:

java.lang.OutOfMemoryError: Metaspace

该应用程序也大量使用反射。我们还使用自定义类加载器。所有这些都在java 7中运行良好。

3 个答案:

答案 0 :(得分:22)

我假设您可以在一段时间内使用相同的请求(请求集)创建问题。 定义MaxMetaspaceSize是一件好事,否则应用程序将使用本机内存,直到它用完为止。 但我将从以下步骤开始:

  1. 检查JVM中加载的类的数量是否会在您多次向服务器发送时为同一请求增长。如果是,您可能正在创建动态类,这将导致在元空间中加载的类增长。那么如何检查加载的类的数量,您可以使用visualvm使用JMX连接到服务器或在本地运行来模拟。 我将提到本地的步骤,但是对于远程连接JMX,您应该将JVM参数添加到应用程序并启动它并在端口9999和远程连接上使用-XX:+ UnlockDiagnosticVMOptions。
  2.    -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,然后查看加载的类数。在那里,您可以监视堆以及元空间。但我会添加其他工具来密切监控元空间。

    1. 此外,一旦连接到jvm,您可能需要获取堆快照并找出使用OQL加载的类。因此,在您进行堆转储之前,请停止对服务器的请求,因此您不会捕获任何飞行请求/执行代码及其关联对象,但这不是必需的。因此,在visualvm内多次运行相同的请求集后,在“监视器”空间中,单击右上角的“堆转储”。然后打开/加载快照,您将看到OQL控制台的选项。你会在permgen分析下的右下方面板上看到一些预定义的OQL查询。运行名为“classloader loaded class histogram”的查询,我想这将给出每个类加载器加载的类的数量。你可以用它来找出哪个类加载器正在加载课程。
    2. 选择map(sort(map(heap.objects('java.lang.ClassLoader'),    '{loader:it,count:it.classes.elementCount}'),'lhs.count< rhs.count“),    'toHtml(it)+“
      ”')

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

      select { loader: cl,
                   classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
          from instanceof java.lang.ClassLoader cl
      
      1. 然后尝试追踪元空间区域的增长。现在我们将使用jconsole和java拥有的新东西:jmc(java任务控制)。您可以使用jconsole连接到jvm(本地或远程),一旦连接到内存选项卡,您就可以监视那里的非堆增长,这应该具有元空间和代码缓存以及压缩类空间。 现在连接
      2.   

        JMC

        连接到VM,然后在连接后单击右侧顶部的JMC中的“诊断命令”。由于我们已启用UnlockDiagnosticVMOptions,因此可以执行GC.class_stats。您可能希望使用show all columns运行它并在csv中打印。所以命令看起来像:

        GC.class_stats -all=true -csv=true
        

        然后,您可以比较不同时期的类统计信息,并找出哪些类导致问题(元空间增长)或哪些类在元空间中具有相关信息(方法/方法数据)。如何分析当时收集的csv输出:我会把csv加载到数据库或其他地方的两个相似的表(代表csv)中以比较GC.class_stats csv输出,我可以运行一些SQL或任何其他分析工具。这样可以更好地了解元空间中的增长情况。 GC类统计信息包含以下列:

          

        索引,超级,InstSize,InstCount,InstBytes,镜像,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,注释,CP,CpTags,CpCache,CpOperands,CpRefMap,CpAll,MethodCount,MethodBytes,ConstMethod,MethodData,StackMap,字节代码,MethodAll,ROAll,RWAll,总计,类名,类加载器

        希望它有所帮助。此外,如果它不会导致1.7中的任何泄漏,则该错误可能出现在Java 8中。

        如果任何人持有对类加载器的任何引用,那么这些类也不会从元空间中卸载。如果您知道您的类加载器应该是GCed并且没有人应该保存对类加载器的引用,您可以返回到visualvm中的堆转储并单击类加载器实例并右键单击以查找“最近的GC根”,这将告诉您你持有类加载器的引用。

答案 1 :(得分:5)

我们有类似的问题,根本原因是60K类文件被加载到元空间内存中,但没有任何内容被卸载。在JVM arg下面添加修复问题。

-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true

https://issues.apache.org/jira/browse/CXF-2939

希望这有帮助。

答案 2 :(得分:0)

如果某些自动部署(例如tomcat),也不要将备份保留在tomcat \ webapps中,否则可能会尝试加载备份并与这些资源冲突。