JVM 参数设置不当导致服务器内存爆满-处理复盘
JVM 参数设置不当导致服务器内存爆满-处理复盘
问题
用户反馈服务挂了, 一看是后端服务器内存爆满了.
先说结论
如果手动指定了 Xmx 参数, 请检查服务器内存是否能承受
如果没有指定 Xmx 参数, jvm 默认会将堆内存上限设定为物理内存/容器内存的四分之一
canal (https://github.com/alibaba/canal) 是一个很棒的项目, 但是现在明显缺乏维护, 新功能开发缓慢, 老功能问题百出. 想参与开源的同学可以关注一下, 大有可为.
分析
这个服务器运行的主要程序:
springboot 的 docker 容器
直接在宿主机上用 java11 运行的 canal 和 canal adapter (用于同步 mysql 数据到 es)
有嫌疑的就是这两个东西.
把服务器重启之后, 内存降到了正常水平, 我记录了此时他们的内存占用情况.
过了几天以后, 我通过腾讯云监控看到了服务器内存占用又在稳步升高
登上服务器看到, springboot 内存占用没有变化, 两个 canal 的内存占用比重启时变多了不少.
打开 canal 的启动脚本, 观察到 Xmx 参数设的很大, 超出了服务器的物理内存上限.
还原事故现场: canal 的堆内存上限设置的很大, 他就不停地申请内存, 也不会触发 gc, 直到把服务器的内存塞满.
解决方案: 把 canal 启动脚本中的 Xmx 参数调低一点, 内存不够就让他自己去 gc
效果:
收获
通过这次事故, 我学习了 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
结论
见开头