DICOM医学影像匿名化导致服务重启的问题

Published on
219 12~16 min

背景

线上环境有一个服务最近频繁发生重启现象,且重启时间没有任何规律

该服务主要是用于一些大文件的处理,比如医学影像匿名化、excel文件导出、文件打包等,该服务使用java语言编写。

关键词:DICOM医学影像,匿名化,OOM,服务重启,dcm4che

问题分析

1.观察线上环境监控指标,分析服务重启时是否存在异常现象

image2024-1-26_17-11-10.png image2024-1-26_17-11-58.png

观察服务重启时的cpu和内存指标,并无明显异常。

2.查询是否导出了堆内存快照信息,分析堆内存信息

堆内存快照导出需要jvm已经配置如下参数

-XX:+HeapDumpOnOutOfMemoryError #当发生oom时生成堆内存快照信息

-XX:+ExitOnOutOfMemoryError #当发生oom时jvm停止运行

-XX:HeapDumpPath=/路径/test.hprof #指定堆内存导出的路径

打开HeapDumpPath文件夹,比较幸运,jvm在重启时生成了快照信息,直接下载下来,使用内存分析工具,可以看到某一个线程的栈信息显示发生了OOM,基本可以断定是处理dcm文件时抛出的异常,该方法是用于匿名化时生成缩略图。

image2024-1-26_17-16-23.png

3.定位问题文件和错误原因

查找重启时间段内执行匿名化的文件操作记录,重试后追踪线上日志,发现如下信息:Requested array size exceeds VM limit

image2024-1-26_17-12-54.png该错误表示程序请求创建的数组超过最大长度限制,发生该错误的原因是JVM会 限制数组可分配的最大长度。将该dcm影像下载到本地进行分析和debug,追踪到发生错误的地方,如下图所示:

image2024-1-26_17-19-24.png由于m的值过小,导致运算后的offset超过了JVM 限制了数组的最大长度。

该offset的值是根据窗宽窗位以及斜率计算的,从下图可以看到,斜率标签Tag.RescaleSlope(标签为:0028,1053)的值确实很小,大小为5.27485 X 10的-11次方,所以在进行offset计算时,根据计算结果值生成的数组的size大于了JVM的最大长度限制,因而发生OOM异常

image2024-1-26_17-33-30.png

解决办法

因为已经确认发生问题的图像没有有效的像素信息,所以直接手动删除问题图像,重新执行匿名化,可以成功且服务未重启。后续可以增加斜率标签的校验规则,值过小,判定为问题图像。