Java:非堆内存分析

23

我们有一个问题,即我们的非堆内存一直在增长。因此,我们必须每三天重启我们的JEE(Java8)Web应用程序(您可以在此处看到屏幕截图:非堆和堆内存的截图)。

我已经尝试找出填充非堆的原因,但是我找不到任何工具来创建非堆转储。您有什么想法,如何调查以找出哪些元素正在越来越多地增长?

Java版本

java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

Tomcat版本

Apache Tomcat Version 7.0.59

您在下面的评论中提到它是一个嵌入式Tomcat。您能否在帖子中添加JVM版本和启动时使用的参数? - Aris2World
Tomcat的版本也很重要。 - Aris2World
谢谢@Stefan,如果您也有嵌入式Tomcat的版本... - Aris2World
自从Tomcat 7.0.59版本以来,已经解决了一些内存泄漏错误。您可以升级它吗?另一种可行的方法是将JVM降级到1.7,验证是否存在永久空间OOM,并在必要时使用非常成熟的工具(如eclipse MAT、jmc等)进行分析。 - Aris2World
3个回答

13

MemoryPoolMXBean提供的非堆内存使用情况计数以下内存池:

  • Metaspace (元空间)
  • Compressed Class Space (压缩类空间)
  • Code Cache (代码缓存)

换句话说,标准的非堆内存统计数据包括编译方法和加载类所占用的空间。增加的非堆内存使用量很可能表示存在类加载器泄漏的问题。

使用方法:

  • 使用jmap -clstats PID命令转储类加载器统计信息;
  • 使用jcmd PID GC.class_stats命令打印每个已加载类的内存使用详细信息。后者需要使用参数-XX:+UnlockDiagnosticVMOptions

1
正如@apangin所指出的,看起来您随着时间的推移使用了更多的Metaspace。这通常意味着您正在加载更多的类。我建议记录正在加载的类和编译的方法,并尝试在生产环境中持续限制此操作的数量。可能您有一个库在不断生成代码但没有清理它。查看正在创建的类可以给您一个提示。

针对非堆本地内存。

您可以使用/proc/{pid}/maps在Linux上查看内存映射。这将让您知道正在使用多少虚拟内存。

您需要确定这是由于以下原因之一:

  • 线程或套接字数量增加
  • 正在使用直接ByteBuffer。
  • 第三方库正在使用本机/直接内存。

从查看您的图表来看,您可以减少堆大小并增加最大直接内存,并将重启时间延长至一周或更长时间,但更好的解决方案是解决原因。


3
看起来,OP的图表显示的是由MemoryPoolMXBean提供的内存使用情况。标准非堆统计数据中不包括任何线程、套接字、直接字节缓冲区或第三方库的内存使用情况。非堆池只包括代码缓存、元空间和压缩类空间。就是这些。 - apangin
1
这是一个有趣的观察。那么它可能指向一个类加载问题(例如部署新的.war或.ear文件,旧版本没有正确释放,或过多的动态字节码生成和加载,或两者结合)。 - Codo
我们使用嵌入式Tomcat运行J2EE Web应用程序,因此没有.war或.ear文件部署。 - stefa ng

0

在Java 8中,类元数据现在位于一个名为Metaspace的非堆内存部分中(而不再是PermGen)。如果您的非堆内存主要被Metaspace占用,您可以使用jstat找出原因。

这不是一个分析非堆内存的通用工具。但它仍然可能对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接