【问题标题】:Java Heap Dump : How to find the objects/class that is taking memory by 1. io.netty.buffer.ByteBufUtil 2. byte[] arrayJava 堆转储:如何找到占用内存的对象/类 1. io.netty.buffer.ByteBuf Util 2. byte[] 数组
【发布时间】:2021-10-11 08:38:36
【问题描述】:

我发现我的一个 spring boot 项目的内存(RAM 消耗)每天都在增加。当我将 jar 文件上传到 AWS 服务器时,它占用了 582 MB 的 RAM(最大分配的 RAM 为 1500 MB),但每天,RAM 增加 50 MB 到 100 MB,5 天后,今天占用了 835 MB .目前该项目有 100-150 个用户,并且正常使用 Rest API。

由于 RAM 的增加,应用程序多次出现以下错误(从日志中发现错误):

Exception in thread "http-nio-3384-ClientPoller" java.lang.OutOfMemoryError: Java heap space

为了解决这个问题,我发现通过使用 JAVA Heap Dump,我可以找到占用内存的对象/类。因此,通过在命令行中使用Jmap,我创建了一个堆转储并将其上传到Heap HeroEclipse Memory Analyzer Tool。在他们两个中,我发现了以下内容:

1. 总浪费内存为:64.69MB (73%)(查看下方截图)

2。其中,Byte [] arrayLinkedHashmap[] 占用了 34.06MB(查看下面的截图),我在整个项目中从未使用过。我在我的项目中搜索了它,但没有找到。

3。以下 2 个大对象分别占用 32 MB 和 20 MB。

1. Java Static io.netty.buffer.ByteBufUtil.DEFAULT_ALLOCATOR

2. Java Static com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.connectionFinalizerPhantomRefs` 

所以我试图找到这个 netty.buffer。在我的项目中,但我没有找到任何与 netty 或 buffer 匹配的东西。

现在我的问题是我怎样才能减少这种内存泄漏我怎样才能找到确切的内存消耗对象/类/变量以便我可以减少堆大小.

我知道很少有专家会要求提供源代码或类似的东西,但我相信从堆转储中我们可以找到内存泄漏或内存中可用的活动对象 .我正在寻找该选项或任何减少此堆转储的方法!

过去 3 周我一直在处理这个问题。任何帮助,将不胜感激。 谢谢!

【问题讨论】:

  • Something 使用了 Netty IO 框架。您所看到的很可能是缓冲区缓存。除非它失去控制,否则你无能为力。 32MB 没有失控。
  • 我会建议一种方法。在下一次回收之后或现在,只对“活动”对象进行一个堆转储。 jmap 有这个选项。一旦您看到 1-2 天后堆使用量上升,请以类似的方式进行另一个转储。继续收集它几天,直到它崩溃。然后比较转储中的对象增长。 MAT可以。如果有泄漏,您应该会看到增长模式。另外请记住,转储“实时”对象是关键。快乐的泄漏狩猎! :)
  • @suv3ndu 谢谢,但上面的图片和解释是活体的。这是我使用“jmap -dump:live,format=b,file=/home/ubuntu/dump 35”的命令,其中 35 是我的进程 ID
  • @KishanSolanki 这些图片来自最终的故障转储吗?如果是这样,对我来说,它们看起来不像是您提到的每天 50-100MB 增长的泄漏对象。这就是我建议这种方法的原因。

标签: java spring-boot out-of-memory heap-dump java-heap


【解决方案1】:

从启用JVM native memory tracker 开始,通过添加标志-XX:NativeMemoryTracking=summary 来了解内存的哪一部分正在增加。根据文档,有一些性能开销 (5-10%),但如果这不是问题,我建议即使在生产环境中运行 JVM 并启用此标志。

然后您可以使用jcmd <PID> VM.native_memory 检查值(这个答案中有一个很好的写法:Java native memory usage

如果确实分配了一大块本机内存,这很可能是由 Netty 分配的。

您如何在 AWS 中运行您的应用程序?如果它在 Docker 映像中运行,您可能会偶然发现这个问题:What would cause a java process to greatly exceed the Xmx or Xss limit? 如果是这种情况,您可能需要设置环境变量MALLOC_ARENA_MAX,如果您的应用程序正在使用本机内存(Netty 使用)并在具有大量内核的服务器上运行。 JVM 完全有可能为 Netty 分配了这块内存,但看不到任何释放它的理由,所以它看起来只会继续增长。

如果你想控制 Netty 可以分配多少本机内存,你可以为此使用 JVM 标志 -XX:MaxDirectMemorySize(我相信默认值与 Xmx 相同)并降低它以防你应用不需要那么多内存。

JVM 内存调整是一个复杂的过程,当涉及本机内存时,它会变得更加复杂 - 正如链接的答案所示,它不像简单地设置 XmsXmx 标志并期望不再有内存那么简单使用。

【讨论】:

    【解决方案2】:

    堆转储不足以检测内存泄漏。 您需要查看调用 GC 后拍摄的两个连续堆快照的差异。 或者您需要一个分析工具来计算每个类的代数。 然后,您应该只查看在 GC 中幸存下来并从旧快照传递到新快照的域对象(不是字节或字符串等通用对象)。 或者,如果使用分析工具,请寻找仍然存在并在许多代中不断增长的旧领域对象。

    让对象存活了很多代并不断增长意味着这些对象仍然被引用并且 GC 无法回收它们。然而,仅仅存在很多代并不足以导致泄漏,因为缓存或静态对象可能会保留很多代。另一个重要因素是它们不断增长。

    在你检测到什么对象被泄露后,你可以使用堆哑函数来分析这些对象并获取引用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 2011-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-20
      • 1970-01-01
      相关资源
      最近更新 更多