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

问题描述

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

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

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

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

其他一些信息:

  • 我使用的是 openjdk-1.8.0
  • 我没有设置任何JVM选项来限制堆大小,启动命令很简单:java -jar my.jar
  • 我尝试使用 jcmd 进行堆转储,转储文件大小只有 15M 左右。
  • 我尝试了 pmap ,但打印的信息似乎太多了,我不知道哪些有用。

解决方法

您可以使用 VisualVM 来监控 JVM 中发生的事情。您还可以使用 JConsole 进行主要概述。它自带 JDK。如果您的 JDK 设置了环境变量,则从终端使用 jconsole 启动它。然后选择您的应用程序并开始监控。

,

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

如果非堆内存占用不多,并且当您使用分析工具查看内存时一切看起来都不错,那么我猜是 JVM 持有内存而不是释放它们。 所以你最好检查一下你的 GC 是否根本没有工作,或者 GC 是否正在使用分析工具强制执行,内存是否下降,它是否扩展或发生了什么。 JVM 内存和堆内存有 2 种不同的行为,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 使用量的原因。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...