Java 内存不足。这不是内存泄漏吗?

Posted

技术标签:

【中文标题】Java 内存不足。这不是内存泄漏吗?【英文标题】:Java runs out of memory. Is this not a memory leak? 【发布时间】:2020-06-27 10:26:39 【问题描述】:

我为我们的 Java 程序分配了 2GB 内存。在特定线程运行的几个小时内,内存稳定且线性地增加,直到 Kubernetes 将其杀死,因为它达到了我分配的 2GB 限制。我们当然想到了内存泄漏,但是我们在 gc 日志中一直看到这样的内容:

[7406.381s][info][gc] GC(8326) Pause Full (System.gc()) 130M->65M(214M) 157.995ms
    既然内存是线性增加的,而这些日志表明堆内存没有增加,那么排查内存泄漏是不是没用? 内存增加的其他可能原因是什么?

一些背景信息:

没有日志表明容器已停止或终止。 k8s 中也没有事件(但是“重启”= 1)。上面的日志行是我们看到(在 Graylog 中)Spring Boot / Tomcat 正在启动(因此它必须已经重新启动)之前的最后一个日志行。我们看到这种情况发生在 Grafana 中内存图达到 2GB 线的时候。如果没有 Grafana,我们可能需要一段时间才能发现它与内存有关。

Kubernetes 部署 yml 部分:

spec:
  template:
    spec:
      containers:
        - name: ... (omitted)
          resources:
            limits:
              cpu: 1200m
              memory: 2Gi
            requests:
              cpu: 50m
              memory: 50Mi

Dockerfile的最后一行:

ENTRYPOINT ["java", "-Xmx2G", "-verbose:gc", "-jar", "/backend.jar"]

其中 "-verbose:gc" 导致日志行与我上面引用的行类似。

重现该问题需要一段时间,但我们重复了几次。

我们使用的是 Java 11。

【问题讨论】:

您可能有内存泄漏。你能附上例如visualvm 到进程可视化? 您的应用程序是否使用任何本机库?也就是依赖一些.so.dll的库?请记住,如果您为应用分配的总内存为 2G,则最大堆大小必须更小,以允许非堆内存。 应用程序的流量是增长还是独立应用程序 @RealSkeptic 表示您的 PermGen 可能会被填满。 @davidmontoyago 没有 PermGen。自 Java 8 以来,它已被 Metaspace 取代。 【参考方案1】:

我认为您根本没有泄漏,您只是使用了错误的选项。使用-Xmx2G,您是在告诉Java 它最多可以为堆使用2G。同时你告诉 Kubernetes 内存的绝对限制是 2Gi。现在,Java 使用不在堆上的内存,所以当它试图将堆扩展到 2G 时,它会耗尽并且 pod 被杀死。

要解决此问题,请确保为堆外的内存留出合理的余量。暂时将 Kubernetes 限制增加到 3G,然后在您知道需要多少本机内存时将其缩小。我猜2.5G是一个合理的水平,但这只是一个猜测。或者,您可以减小 Java 堆大小并使用 1.5G(或更少)的堆运行,为本地内存留出一些空间。

【讨论】:

值得一提的是,Java K,M,G 实际上是 Ki,Mi,Gi。也就是说,-xmx2G 与分配的 2Gi 相同。

以上是关于Java 内存不足。这不是内存泄漏吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java 字符串内存泄漏

缓解CLion因内存不足卡顿的问题

Java内存不足怎么办?

如何避免内部类中的内存泄漏

这是java内存泄漏吗

Java进程内存泄漏判断及解决方法