9

我发现我的一个 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 . 其中,34.06MB 被Byte [] arrayand占用LinkedHashmap[](查看下面的截图),我在整个项目中从未使用过。我在我的项目中搜索了它,但没有找到。

在此处输入图像描述 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 或缓冲区匹配的东西。

现在我的问题是如何减少这种内存泄漏,或者如何找到确切的内存消耗对象/类/变量,以便减少堆大小。

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

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

4

2 回答 2

4

从启用JVM 本机内存跟踪器开始,通过添加标志来了解哪一部分内存正在增加-XX:NativeMemoryTracking=summary。根据文档 (5-10%) 有一些性能开销,但如果这不是问题,我建议即使在生产中也可以在启用此标志的情况下运行 JVM。

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

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

您如何在 AWS 中运行您的应用程序?如果它在 Docker 映像中运行,您可能会偶然发现这个问题:什么会导致 java 进程大大超过 Xmx 或 Xss 限制?MALLOC_ARENA_MAX如果是这种情况,如果您的应用程序使用本机内存(Netty 使用)并在具有大量内核的服务器上运行 ,您可能需要设置环境变量。JVM 完全有可能为 Netty 分配了这些内存,但看不到任何释放它的理由,所以它似乎只会继续增长。

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

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

于 2021-08-10T12:54:28.590 回答
1

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

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

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

于 2021-08-15T00:27:26.260 回答