JVM 参数设置不当导致服务器内存爆满-处理复盘

问题

用户反馈服务挂了, 一看是后端服务器内存爆满了.

先说结论

  • 如果手动指定了 Xmx 参数, 请检查服务器内存是否能承受

  • 如果没有指定 Xmx 参数, jvm 默认会将堆内存上限设定为物理内存/容器内存的四分之一

  • canal (https://github.com/alibaba/canal) 是一个很棒的项目, 但是现在明显缺乏维护, 新功能开发缓慢, 老功能问题百出. 想参与开源的同学可以关注一下, 大有可为.

分析

这个服务器运行的主要程序:

  • springboot 的 docker 容器

  • 直接在宿主机上用 java11 运行的 canal 和 canal adapter (用于同步 mysql 数据到 es)

有嫌疑的就是这两个东西.

把服务器重启之后, 内存降到了正常水平, 我记录了此时他们的内存占用情况.

过了几天以后, 我通过腾讯云监控看到了服务器内存占用又在稳步升高

image-20230924111827743

登上服务器看到, springboot 内存占用没有变化, 两个 canal 的内存占用比重启时变多了不少.

打开 canal 的启动脚本, 观察到 Xmx 参数设的很大, 超出了服务器的物理内存上限.

还原事故现场: canal 的堆内存上限设置的很大, 他就不停地申请内存, 也不会触发 gc, 直到把服务器的内存塞满.

解决方案: 把 canal 启动脚本中的 Xmx 参数调低一点, 内存不够就让他自己去 gc

效果:

image-20230924112835356

收获

通过这次事故, 我学习了 java 内存限制相关的知识

  • 如果不指定 Xmx 参数, 堆内存上限默认是物理内存的四分之一.

  • 在 JDK 8u130 之前, 如果对 docker 容器进行了内存限制, jvm 是不会遵守的.

    比如物理内存是 8g, docker 内存限制是 1g, jvm 还是会把堆内存上限设定为 8g /4 = 2g, 而不会去遵守 docker 容器的内存限制 1g

    在 JDK 8u130 之后, jvm 会把 docker 容器的内存限制当做物理内存上限, 对于上面这个例子来说, jvm 堆内存上限就会是 1g / 4 = 256mb

参考自: https://www.baeldung.com/ops/docker-jvm-heap-size

结论

见开头

文章作者: 白烛魁
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 白烛魁的小站
Java
喜欢就支持一下吧