docker - Kubernetes 为运行 JVM 的 pod 抛出 OOM
问题描述
我正在运行包含 JVM (java8u31) 的 Docker 容器。这些容器被部署为 Kubernetes 集群中的 pod。通常我会为 Pod 获取 OOM,然后 Kubernetes 会杀死 Pod 并重新启动它。由于我是 Kubernetes 新手,因此在寻找这些 OOM 的根本原因时遇到了问题。
这是JVM参数
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Xms700M -Xmx1000M -XX:MaxRAM=1536M -XX:MaxMetaspaceSize=250M
这些容器被部署为有状态集,以下是资源分配
resources: requests: memory: "1.5G" cpu: 1 limits: memory: "1.5G" cpu: 1
因此分配给容器的总内存与 MaxRam 匹配
如果我使用
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/etc/opt/jmx/java_pid%p.hprof
它并没有帮助,因为一旦出现 OOM,pod 就会被杀死并重新创建并启动,因此 pod 中的所有内容都会丢失获取线程或 HEAP 转储的唯一方法是通过 SSH 连接到 pod,这也是我无法使用的,因为 pod 是在 OOM 之后重新创建的,所以在 OOM 时我没有得到内存占用。我在 OOM 之后 SSH,这并没有多大帮助。
我还使用 visualVM、jHat 对代码进行了分析,但找不到大量内存占用,这可能导致 JVM 中运行的线程消耗过多内存或可能存在泄漏的结论。
感谢任何帮助解决 Kubernetes 引发的 OOM。
解决方案
当 Pod 中的应用程序达到您通过 resources.limits.memory 或命名空间限制设置的内存限制时,Kubernetes 会重新启动 Pod。
Kubernetes限制资源的部分在以下文章中有所描述:
Java 应用程序消耗的内存不限于您可以通过指定选项设置的堆大小:
-Xmssize Specifies the initial heap size.
-Xmxsize Specifies the maximum heap size.
Java 应用程序需要一些额外的内存来存储元空间、类空间、堆栈大小,而 JVM 本身需要更多的内存来完成垃圾收集、JIT 优化、堆外分配、JNI 代码等任务。很难以合理的精度预测 JVM 的总内存使用量,因此最好的方法是在具有通常负载的实际部署中对其进行测量。
我建议您将 Kubernetes pod 限制设置为两倍Xmx
大小,检查您是否不再出现 OOM,然后逐渐减小到开始出现 OOM 的程度。最终值应该在这些点之间的中间。
您可以从 Prometheus 等监控系统中的内存使用统计信息中获得更精确的值。
另一方面,您可以尝试通过指定可用选项的数量来限制 java 内存使用,如下所示:
-Xms<heap size>[g|m|k] -Xmx<heap size>[g|m|k]
-XX:MaxMetaspaceSize=<metaspace size>[g|m|k]
-Xmn<young size>[g|m|k]
-XX:SurvivorRatio=<ratio>
可以在这些文章中找到更多详细信息:
限制 JVM 内存使用的第二种方法是根据 RAM(或 MaxRAM)的数量计算堆大小。在文章中有一个很好的解释它是如何工作的:
默认大小基于机器上的内存量,可以使用
-XX:MaxRAM=N
标志设置。通常,该值由 JVM 通过检查机器上的内存量来计算。但是,JVM 限制MaxRAM
为1 GB
客户端编译器、4 GB
32 位服务器编译器和128 GB
64 位编译器。最大堆大小是MaxRAM
. 这就是为什么默认堆大小可以变化的原因:如果机器上的物理内存小于MaxRAM
,则默认堆大小是它的四分之一。但即使有数百 GB 的 RAM 可用,JVM 默认使用的最多也是32 GB
:四分之一的128 GB
. 默认的最大堆计算实际上是这样的:
Default Xmx = MaxRAM / MaxRAMFraction
因此,也可以通过调整 -
XX:MaxRAMFraction=N
标志的值来设置默认的最大堆,默认为4
. 最后,为了让事情变得有趣,该-XX:ErgoHeapSizeLimit=N
标志也可以设置为 JVM 应该使用的最大默认值。该值是0
默认值(意味着忽略它);否则,如果小于 ,则使用该限制MaxRAM / MaxRAMFraction
。初始堆大小的选择是相似的,尽管它的复杂性更少。初始堆大小值是这样确定的:
Default Xms = MaxRAM / InitialRAMFraction
从默认的最小堆大小可以得出结论,
InitialRAMFraction
标志的默认值为64
. 如果该值小于5 MB
- 或者严格来说,小于由-XX:OldSize=N
(默认为4 MB
)加上 -XX:NewSize=N
(默认为1 MB
)指定的值,则会出现这里的一个警告。在这种情况下,新旧大小的总和用作初始堆大小。
本文为您提供了一个开始为面向 Web 的应用程序调整 JVM 的好方法:
推荐阅读
- docker - Docker 容器失败,找不到文件
- javascript - 无法从 jquery 函数在 textarea 中添加###Name###
- python - pandas groupby 中未来行的条件搜索
- amazon-web-services - SES 模板/电子邮件无法呈现日文文本
- javascript - 如何在 react.js 中使用 iframe
- postgresql - postgresql 重建索引和重新创建索引有什么区别,哪个更好?
- python - Python:使用多线程修改 pandas DataFrame 时,Spyder 发生错误
- cloudera - Cloudera Navigator 中的沿袭功能
- html - 如何通过 StringBuilder 在 html 中添加图像
- html - 引导类未按预期工作