记一次生产环境OOM的排查

date
Dec 9, 2021
slug
记一次生产环境OOM的排查
status
Published
tags
Java
summary
公司有个项目,正常发版的过程中,有个自动化测试的流程,如果测试不通过,就会导致发版失败。为了测试方便,项目中留有内部访问的 REST API,仅供相关的测试代码调用。近期发版非常不稳定,经常报这些测试相关的 API 502 Bad Gateway,并且同时可能出现 Canary 实例的 OOM 报警。
type
Post

Background

公司有个项目,正常发版的过程中,有个自动化测试的流程,如果测试不通过,就会导致发版失败。为了测试方便,项目中留有内部访问的 REST API,仅供相关的测试代码调用。近期发版非常不稳定,经常报这些测试相关的 API 502 Bad Gateway,并且同时可能出现 Canary 实例的 OOM 报警。

Research

拿到 Heap Dump 的过程比较曲折,这里就不说了,主要是需要在发版之前留心,在问题出现之后立刻去进行 dump ,否则现场被销毁掉就无从下手了。主要说下拿到 dump 文件之后应该如何分析。
如果对项目情况不够了解,建议最好拿两个 dump,正常状态和 OOM 发生时的状态,这样有对比,可以更容易的发现问题。
abnormal
abnormal
normal
normal
可以看到,正常状态下,比较大的对象是DefaultListableBeanFactoryParallelWebappClassLoader
异常状态下,最大的对象是个ScheduledThreadPoolExecutor
线程池占用的资源得不到释放导致 OOM 是非常有可能的,所以我接下来详细看一下这个线程池里都有什么东西。
notion image
可以看到,线程池占用的内存,基本都被DelayedWorkQueue占用,这个Queue中,包含若干个ScheduledFutureTask,其中可以看到相关的项目代码(TestSubscriptionController),也可以看到占内存最多的是Subscription对象(也是项目代码)。
由于出现了项目代码的线索,接下来我们可以从代码方面尝试分析问题了
notion image
测试逻辑中,会调用这个方法,一共有两个线程池executorServicescheduledExecutorService,主要逻辑在executorService中,sheduledExecutorService是作为保底机制,释放worker中占用的资源,避免影响其他的 test case。
notion image
notion image
这里有两个问题,第一,sheduledExecutorService这个线程池是个SingleThreadScheduledExecutor,所以其中所有逻辑是串行的;第二,schedule执行的 delay 是比较久的,有6分钟。
所以如果在测试中,有大量的 worker 被 submit,scheduledExecutorService内的任务又得不到执行,导致 worker 占用的对象不能被 GC,导致 OOM。这也解释了为什么 OOM 总会在相关 API 报502 之后出现,因为heap中可用内存越来越小,JVM 会不断的尝试 GC,但是因为绝大多数内存都无法 GC,JVM 没有资源去执行正常的业务逻辑,即使执行,也会不断的创建新的任务放到线程池中,导致问题更加严重。

Solution

在跟相关同事确认之后,采用了其他方式进行清理工作,而不是采用线程池的方式。再次尝试发版,没有 OOM 的问题出现了。
 

© oddcc 2020 - 2024