写点什么

为什么 Java 容器推荐使用 ExitOnOutOfMemoryError 而非 HeapDumpOnOutOfMemoryError?

用户头像
东风微鸣
关注
发布于: 2020 年 10 月 22 日
为什么Java容器推荐使用ExitOnOutOfMemoryError而非HeapDumpOnOutOfMemoryError?

前言

好久没写文章了, 今天之所以突然心血来潮, 是因为昨天出现了这样一个情况:



我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露, 导致OutOfMemoryError, 但是因为经过我们精心优化的openjdk容器参数, 这次故障对用户完全无感知. 💪💪💪



那么我们是如何做到的呢?



HeapDumpOnOutOfMemoryError VS ExitOnOutOfMemoryError

我们都知道, 在传统的虚拟机上部署的Java实例. 为了更好地分析问题, 一般都是要加上: -XX:+HeapDumpOnOutOfMemoryError这个参数的. 加这个参数后, 如果遇到内存溢出, 就会自动生成HeapDump, 后面我们可以拿到这个HeapDump来更精确地分析问题.



但是, "大人, 时代变了!"



容器技术的发展, 给传统运维模式带来了巨大的挑战, 这个挑战是革命性的:



  1. 传统的应用都是"永久存在的" vs 容器pod是"短暂临时的存在"

  2. 传统应用扩缩容相对困难 vs 容器扩缩容丝般顺滑

  3. 传统应用运维模式关注点是:"定位问题" vs 容器运维模式是: "快速恢复"

  4. 传统应用一个实例报HeapDumpError就会少一个 vs 容器HeapDump shutdown后可以自动启动, 已达到指定副本数

  5. ...



简单总结一下, 在使用容器平台后, 我们的工作倾向于:



  1. 遇到故障快速失败

  2. 遇到故障快速恢复

  3. 尽量做到用户对故障"无感知"



所以, 针对Java应用容器, 我们也要优化以满足这种需求, 以OutOfMemoryError故障为例:



  1. 遇到故障快速失败, 即尽可能"快速退出, 快速终结"

  2. 有问题java应用容器实例退出后, 新的实例迅速启动填补;

  3. "快速退出, 快速终结", 同时配合LB, 退出和冷启动的过程中用户请求不会分发进来.



-XX:+ExitOnOutOfMemoryError就正好满足这种需求:



传递此参数时,抛出OutOfMemoryError时JVM将立即退出。 如果您想终止应用程序,则可以传递此参数。



细节

让我们重新回顾故障: "我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露, 导致OutOfMemoryError"



该customer应用概述如下:



  1. 无状态

  2. 通过Deployment部署, 有6个副本

  3. 通过SVC提供服务



完整的过程如下:



  1. 6个副本, 其中1个出现OutOfMomoryError

  2. 因为副本的jvm参数配置有: -XX:+ExitOnOutOfMemoryError, 该实例的JVM(PID为1)立即退出.

  3. 因为pid 1进程退出, 此时pod立刻出于Terminating状态, 并且变为:Terminated

  4. 同时, customer的SVC 负载均衡会将该副本从SVC 负载均衡中移除, 用户请求不会被分发到该节点.

  5. K8S检测到副本数和Deployment replicas不一致, 启动1个新的副本.

  6. 待新的部分Readiness Probe 探测通过, customer的SVC负载均衡将这个新的副本加入到负载均衡中, 接收用户请求.



在此过程中, 用户基本上是对后台故障"无感知"的.



当然, 要做到这些, 其实JVM参数以及启动脚本中, 还有很多细节和门道. 如: 启动脚本应该是: exec java ....$*



有机会再写文章分享.



新的疑问

上边一章, 我们解释了"为什么Java容器推荐使用ExitOnOutOfMemoryError而非HeapDumpOnOutOfMemoryError", 但是细心的小伙伴也会发现, 新的配置也会带来新的问题, 比如:



  1. JVM从fullgc -> OutOfMemoryError 这段时间内, 用户的体验还是会下降的, 怎么会是"故障无感知"呢?

  2. 用"ExitOnOutOfMemoryError"代替"HeapDumpOnOutOfMemoryError", 那我怎么定位该问题的根因并解决? 2个参数一起用不是更香么?



这些其实可以通过其他手段来解决:



  1. JVM从fullgc -> OutOfMemoryError 这段时间内, 用户的体验还是会下降的, 怎么会是"故障无感知"呢?

  2. 用"ExitOnOutOfMemoryError"代替"HeapDumpOnOutOfMemoryError", 那我怎么定位该问题的根因并解决? 2个参数一起用不是更香么?



readinessProbe:
httpGet:
path: /actuator/info
port: 8088
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 3
periodSeconds: 10
successThreshold: 1
failureThreshold: 3



总结

新的技术带来新的变革, 我们需要以发展的眼光看待"最佳实践, 最佳配置".



2016年, 针对虚机部署的Java的最优参数, 在今天来看, 并不一定仍是最优解.



发布于: 2020 年 10 月 22 日阅读数: 49
用户头像

东风微鸣

关注

资源共享, 天下为公! 2018.11.08 加入

APM行业认证专家, 容器技术认证专家. 现任中国大地保险PAAS平台架构师. 公众号:东风微鸣技术博客

评论

发布
暂无评论
为什么Java容器推荐使用ExitOnOutOfMemoryError而非HeapDumpOnOutOfMemoryError?