0

我有一个 Spring API,大量使用部署在 kubernetes 集群上的内存。

我将自动缩放(HPA)配置为将内存消耗视为缩放标准,并运行负载测试在扩大时一切正常,但是在缩小时内存不会下降,因此创建的 pod 不会被删除。如果我再次运行测试,将创建新的 pod,但不会删除

使用可视 VM 进行本地分析,我认为问题与 GC 有关。本地 GC 在测试期间正常工作,但在请求结束时它停止运行并留下垃圾,并且在很长一段时间后才再次运行。所以我相信留下的这些垃圾正在阻止 HPA 缩小规模。

有没有人对可能导致这种效果的原因或我可以尝试的东西有任何提示?

PS。在分析器中,我没有任何内存泄漏的迹象,当我手动运行 GC 时,剩下的垃圾被删除了

以下是一些额外的细节:

  • Java 版本:11
  • 春季版:2.3
  • Kubernetes 版本:1.17
  • Docker 镜像:openjdk:11-jre-slim
  • HPA 请求内存:1Gi
  • HPA 限制内存:2Gi
  • HPA 内存利用率指标:80%
  • HPA 最小豆荚:2
  • HPA 最大吊舱数:8
  • JVM OPS:-Xms256m -Xmx1G

负载测试后的 Visual VM

负载测试后新的 Relic Memory 驻留

4

1 回答 1

0

很可能没有内存泄漏。

JVM 向操作系统请求内存,上限为-Xmx...命令行选项设置的限制。每次主要 GC 运行后,JVM 都会查看正在使用的堆内存与(当前)堆大小的比率:

  • 如果比率太接近 1(即堆太满),JVM 会向操作系统请求内存以使堆变大。它“热切”地做到了这一点。

  • 如果配给太接近 0(即堆太大),JVM可能会缩小堆并将一些内存返回给 OS。它“不情愿地”这样做。具体来说,在 JVM 决定释放内存之前,它可能需要运行许多完整的 GC。

我认为您所看到的是 JVM 堆大小策略的影响。如果 JVM 处于空闲状态,将没有足够的 full GC 来触发 JVM 收缩堆,并且不会将内存还给 OS。

System.gc()您可以尝试通过调用几次来鼓励 JVM 归还内存。但是运行完整的 GC 是 CPU 密集型的。如果您确实设法让 JVM 缩小堆,那么再次扩展堆(针对下一个大请求)将需要更多完整的 GC。

所以我的建议是:不要那样做。使用其他一些标准来触发您的自动缩放......如果它有意义的话。


另一件需要注意的是,JVM + 应用程序可能会使用大量的非堆内存;例如可执行和共享本机库、本机 (C++) 堆、Java 线程堆栈、Java 元空间等。这些用法都不受该-Xmx选项的限制。

于 2021-04-06T11:38:05.307 回答