Struts文件上传报OutOfMemoryError问题分析
好久没有更新博客了,最近项目也接近尾声了,今天记录一个case处理过程。
?
一、问题描述
1. 异常信息
?
?1) 页面上有一个文件导入功能,可以导入IP信息列表,用户可以通过">>"和“<<”按钮对导入的信息进行添加和删除。
2) 当从文件中导入5000条IP信息时,数据导入过程可以从正常完成,导入后的数据也能正常显示。
3) 当将导入的5000条数据全部删除时,抛出上述OutOfMemoryError异常。
?
二、问题分析
第一次QA发现这个问题时,我想当然以为这是由于导入的数据量太大,导致内存用尽,从而报出这个错误。因为之前版本也有这个问题,所以优先级比较低。直到最近项目接近尾声,经老板提醒,IP地址信息每个存储都小于128byte, 5000个IP地址信息总共也只有500k, 为什么会导致内存耗尽呢? 此事背后一定隐藏着一个天大的秘密!
?
三、分析过程
1) 通过VisualVM连接tomcat进程,观察内存使用情况。
?
?通过几次测试,发现每次提交删除大量IP地址数据时,内存会突然增大,从而导致OutOfMemoryError,而过段时间,通过垃圾回收,内存会重新回收。?
2)排查代码
我在代码中处理IP信息删除的Action里设置断点,却发现在到达这个断点之前,已经抛出OutOfMemoryError,从而排除由于代码中创建大量对象导致OutOfMemoryError的可能性。
?
3)? 通过观察异常堆栈,初步推断: Struts在处理Multipart request时,调用fileupload组件。 fileupload组件在创建流的过程中,内存不足导致OutOfMemoryError异常。
通过查看jsp文件,发现提交的form定义如下:
由此可见Struts调用fileupload来处理multipart request可能有问题。 在Apache官网查阅了issue列表,只找到一个类似的issue: STR-1857. 这个issue中提到fileupload模块本身有缺陷,但没有提及详细原因。所以决定从源码入手进行分析。
?
四、源码分析
从官网上下载了Struts1.2.7和fileupload1.0的源码,导入IDE中开始调试。
1) 找到Struts调用fileupload进行Multipart Request解析的代码:
?
?2) 查看upload.parseRequest(request)的具体处理过程。?代码的注释中我已经标明了处理过程中的关键四步,既然报OutOfMemoryError,肯定和内存分配有关系。?3) 查看第二步中为每个FileItem创建OutStream的代码。?五、结果分析通过上面代码可以看出,FileUpload为每个FormField分配了256k的内存,用于存储parameter value,供后续与Struts框架数据交换。 如果Form表单中有1000个参数,将会使用256M内存。 我测试时导入的IP地址信息数量在4000左右,将消耗1G左右的内存,所以导致出现OutOfMemoryError异常。?我查阅了FileUpload的issue列表,找到了相关的case: FILEUPLOAD-59. 可以看到这个问题在FileUpload 1.1-dev版本中已经修复。 我下载了FileUpload1.1.1版本的源码,看到DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD初始化为10240byte(10k), 相比原来的256k默认值已经大大减少。?通过将Struts升级到1.3.10(包含的FileUpload版本为1.1.1),该问题解决。??ps. 维护遗留系统的孩纸你伤不起,到处都是坑!
?
?
??