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 内存不足。这不是内存泄漏吗?的主要内容,如果未能解决你的问题,请参考以下文章