Java 进程占用的 RAM 比堆大小多得多
Java process takes much more RAM than heap size
我有一个 Java 程序已经 运行 好几天了,它处理传入的消息并将它们转发出去。
我今天注意到的一个问题是,我通过 Runtime.totalMemory() 打印的堆大小仅显示 ~200M,但 top 命令中的 RES 列显示了它占用1.2g内存
程序没有使用直接字节缓冲区。
我如何找出 JVM 占用这么多额外 RAM 的原因?
一些其他信息:
- 我正在使用 openjdk-1.8.0
- 我没有设置任何JVM选项来限制堆大小,启动命令很简单:java -jar my.jar
- 我尝试使用jcmd进行堆转储,转储文件大小只有15M左右。
- 我试了pmap ,但是好像打印的信息太多了,不知道哪个有用。
您可以使用 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 使用情况的原因。
我有一个 Java 程序已经 运行 好几天了,它处理传入的消息并将它们转发出去。
我今天注意到的一个问题是,我通过 Runtime.totalMemory() 打印的堆大小仅显示 ~200M,但 top 命令中的 RES 列显示了它占用1.2g内存
程序没有使用直接字节缓冲区。
我如何找出 JVM 占用这么多额外 RAM 的原因?
一些其他信息:
- 我正在使用 openjdk-1.8.0
- 我没有设置任何JVM选项来限制堆大小,启动命令很简单:java -jar my.jar
- 我尝试使用jcmd进行堆转储,转储文件大小只有15M左右。
- 我试了pmap ,但是好像打印的信息太多了,不知道哪个有用。
您可以使用 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
.
然后当你的进程是运行时,你可以通过执行以下命令来获取统计信息:
jcmd [pid] VM.native_memory
这将生成详细的输出列表,例如堆大小、元空间大小以及直接在堆上分配的内存。
您还可以使用此工具创建基线来监控一段时间内的分配情况。
正如您将能够看到使用此工具一样,JVM 默认为元空间保留大约 1GB,即使可能只使用一小部分。但这可能是您看到的 RSS 使用情况的原因。