Java 进程占用的 RAM 比堆大小多得多

Java process takes much more RAM than heap size

我有一个 Java 程序已经 运行 好几天了,它处理传入的消息并将它们转发出去。

我今天注意到的一个问题是,我通过 Runtime.totalMemory() 打印的堆大小仅显示 ~200M,但 top 命令中的 RES 列显示了它占用1.2g内存

程序没有使用直接字节缓冲区。

我如何找出 JVM 占用这么多额外 RAM 的原因?

一些其他信息:

您可以使用 VisualVM 来监控 JVM 内部发生的事情。您还可以使用 JConsole 进行初步概述。它本身带有 JDK。如果您的 JDK 设置了环境变量,则使用 jconsole 从终端启动它。然后 select 您的应用程序并开始监控。

一件事是,如果您的堆没有占用太多内存,那么请使用分析器工具检查您的非堆内存占用了多少内存。如果该数量很高,甚至在 GC 循环之后,如果它没有下降,那么您可能应该寻找内存泄漏(非堆)。

如果非堆内存占用不多,并且当您使用分析工具查看内存时一切看起来都很好,那么我猜它是持有内存而不是释放内存的 JVM。 因此,您最好检查一下您的 GC 是否根本没有工作,或者是否正在使用分析工具强制执行 GC,内存是否下降、是否扩展或发生了什么。 JVM 内存和堆内存有两种不同的行为,JVM 可以假设它应该在基于

的 GC 周期后扩展
-XX:MinHeapFreeRatio=
-XX:MaxHeapFreeRatio=

以上参数。所以这背后的基本概念是,在一个 GC 周期之后,JVM 开始获取空闲内存和已用内存的测量值,并根据上述 JVM 标志的值开始扩展或缩小自身。默认情况下,它们设置为 40 和 70,您可能有兴趣对其进行调整。这在容器化环境中尤其重要。

Java Native Memory Tracking 工具在这种情况下非常有用。您可以通过使用标志 -XX:NativeMemoryTracking=summary.

启动 JVM 来启用它

然后当你的进程是运行时,你可以通过执行以下命令来获取统计信息:

jcmd [pid] VM.native_memory

这将生成详细的输出列表,例如堆大小、元空间大小以及直接在堆上分配的内存。

您还可以使用此工具创建基线来监控一段时间内的分配情况。

正如您将能够看到使用此工具一样,JVM 默认为元空间保留大约 1GB,即使可能只使用一小部分。但这可能是您看到的 RSS 使用情况的原因。