【问题标题】:Why does idle Java application generate 20 MB garbage in 4 minutes?为什么空闲的 Java 应用程序会在 4 分钟内产生 20 MB 的垃圾?
【发布时间】:2025-12-15 14:30:01
【问题描述】:

我通过使用 ExecutorService 使用 50 个线程在缓存中加载 5000 个对象来测试 Guava 缓存的性能。我想看看缓存对象占用的堆。

看起来缓存对象占用了 25MB。

后来,我在主线程上放了一个 Thread.sleep 让应用程序休眠 10 分钟,这样我就可以对内存等进行采样了。

令人惊讶的是,即使应用程序处于睡眠状态,它也会每 4 分钟产生 20MB 的垃圾。由于每 4 分钟进行一次 GC 操作,因此我在已用堆图中看到了锯齿波。

为什么 java 在空闲应用程序中每分钟会产生 5MB 的垃圾?

更新: 我只运行了一个 main 方法,只调用了 Thread.sleep 10 分钟。

内存分配为每分钟 4 MB。

以下是内存采样器屏幕截图。 看起来只有导致所有活动的 RMI TCP 连接线程。

仅仅是我们观察到的 VisualVM 导致了所有这些活动的事实吗?还是常规的 jvm 活动?

【问题讨论】:

  • 主线程休眠时9个守护线程在做什么?
  • 更重要的是,最好不要再关心这些细节了。某处某处的某个线程正在运行一些预定的代码或类似的代码,并每秒产生 80 KB 的垃圾。它可能是与 VisualVM 通信的线程。这不会影响您的应用程序的整体行为。
  • ExecutorService 50 个线程于下午 3:28 完成并终止。 JVM 本身有 10 个守护线程。应用程序只有 1 个活动的非守护线程。那一个线程睡了大约 10 分钟。
  • @MarkoTopolnik 我同意你的看法。我可以忽略它,因为它不会产生任何问题。令人惊讶的是,我的 5000 个对象对缓存仅占用 5MB,而 JVM 每分钟产生 5MB 垃圾。
  • 保留内存和垃圾之间存在巨大差异。短期对象收集起来非常便宜,许多 Java 习惯用法自然地涉及到此类对象。

标签: java garbage-collection visualvm


【解决方案1】:

您使用 VisualVM 来监控您的应用程序。 VisualVM 使用 JMX/MBeans 来获取监控信息。 JMX 使用 RMI 作为传输层。 VisualVM 会定期轮询 MBean,这会转化为通过 RMI 调用运行在 JVM 上的服务。

每分钟 4 MiB 听起来是 VisualVM 的合理数字。

我建议您使用SJK ttop 命令监控您的进程。它显示每个线程的内存分配率,因此您可以验证是否所有垃圾都可以归因于 RMI 线程,或者您的任何应用程序线程都在做一些乱扔垃圾。

【讨论】:

    【解决方案2】:

    作为底线:没有人可以在不分析您的应用程序的情况下回答这样的问题。您需要在可视 VM 中拍摄内存快照并检查哪些类占用了大量内存。它还有助于对对象计数进行排序,并查看在 GC 之前和之后哪些对象的实例计数变化最多。

    快速浏览一下,由于您的图片中显示了 11 个线程,因此至少您将 Runnable 对象的节点对象提交到您的 ExecutorService,这是大多数当前 Java 库和包都会选择的。

    【讨论】:

    • 我的执行器服务在下午 3:28 完成并终止了所有 50 个线程。这是一个测试应用程序......没有其他处理正在进行。只有一个应用程序线程处于睡眠状态 10 分钟,并且可能由 JVM 启动了大约 10 个守护线程。
    • @Teddy 没有剖析没有人可以回答你,只能自己做......
    • 我运行了内存采样器并附上了截图
    • @Teddy - 随附的屏幕截图回答了您的问题。