【问题标题】:Java process takes much more RAM than heap sizeJava 进程占用的 RAM 比堆大小多得多
【发布时间】:2021-04-03 07:07:43
【问题描述】:

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

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

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

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

其他一些信息:

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

【问题讨论】:

  • JVM 可能没有将之前分配的内存释放回操作系统。
  • 意思是JVM已经收缩了堆,但它仍然持有收缩的空间?它何时将其释放回操作系统? :)
  • 这取决于 JVM 版本和配置。我建议您考虑至少迁移到 java 11。

标签: java heap-memory ram


【解决方案1】:

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

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

jcmd [pid] VM.native_memory

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

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

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

【讨论】:

    【解决方案2】:

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

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

    -XX:MinHeapFreeRatio=
    -XX:MaxHeapFreeRatio=
    

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

    【讨论】:

    • 谢谢。您提供的 2 个标志指定 JVM 扩展或收缩堆的时间。从打印的堆大小(Runtime.totalMemory())来看,看起来堆已经缩小了,但 JVM 仍然持有内存。如果这两个标志可以使JVM释放收缩的内存,我不太有信心.. :)
    • 这些参数不会缩小你的堆(堆通过 GC 循环减少),这些参数是关于 JVM 总提交内存(堆 + 非堆 + ..)。即使堆非常低,如果您不调整这些参数,提交的内存也可能非常高。所以这就是为什么调整它们是有意义的。
    【解决方案3】:

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

    【讨论】:

    • 谢谢。 JDK附带的VisualVM和其他工具监视堆和元空间使用情况,在我的情况下似乎很好,除了这两个之外我需要查看额外的RAM。
    猜你喜欢
    • 2013-06-28
    • 1970-01-01
    • 2021-05-13
    相关资源
    最近更新 更多