好久没有更新博客了,最近项目也接近尾声了,今天记录一个case处理过程。
一、问题描述
1. 异常信息
java.lang.OutOfMemoryError: Java heap space java.io.ByteArrayOutputStream.<init>(Unknown Source) org.apache.commons.fileupload.DeferredFileOutputStream.<init>(DeferredFileOutputStream.java:131) org.apache.commons.fileupload.DefaultFileItem.getOutputStream(DefaultFileItem.java:558) org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:406) org.apache.struts.upload.CommonsMultipartRequestHandler.handleRequest(CommonsMultipartRequestHandler.java:193) org.apache.struts.util.RequestUtils.populate(RequestUtils.java:443) org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:804) org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:203) org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196) org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) javax.servlet.http.HttpServlet.service(HttpServlet.java:637) com.xxxx.ui.framework.ActionServlet.service(ActionServlet.java:138) javax.servlet.http.HttpServlet.service(HttpServlet.java:717) com.xxxx.ui.framework.SessionFilter.doFilter(SessionFilter.java:89)
2. 问题重现过程
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定义如下:
<html:form action="/saveSMTPConn.action" enctype="multipart/form-data">
由此可见Struts调用fileupload来处理multipart request可能有问题。 在Apache官网查阅了issue列表,只找到一个类似的issue: STR-1857. 这个issue中提到fileupload模块本身有缺陷,但没有提及详细原因。所以决定从源码入手进行分析。
四、源码分析
从官网上下载了Struts1.2.7和fileupload1.0的源码,导入IDE中开始调试。
1) 找到Struts调用fileupload进行Multipart Request解析的代码:
public void handleRequest(HttpServletRequest request) throws ServletException { // Get the app config for the current request. ModuleConfig ac = (ModuleConfig) request.getAttribute( Globals.MODULE_KEY); // Create and configure a DIskFileUpload instance. DiskFileUpload upload = new DiskFileUpload(); // The following line is to support an "EncodingFilter" // see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=23255 upload.setHeaderEncoding(request.getCharacterEncoding()); // Set the maximum size before a FileUploadException will be thrown. upload.setSizeMax(getSizeMax(ac)); // Set the maximum size that will be stored in memory. upload.setSizeThreshold((int) getSizeThreshold(ac)); // Set the the location for saving data on disk. upload.setRepositoryPath(getRepositoryPath(ac)); // Create the hash tables to be populated. elementsText = new Hashtable(); elementsFile = new Hashtable(); elementsAll = new Hashtable(); // Parse the request into file items. List items = null; try { items = upload.parseRequest(request); } catch (DiskFileUpload.SizeLimitExceededException e) { // Special handling for uploads that are too big. request.setAttribute( MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED, Boolean.TRUE); return; } catch (FileUploadException e) { log.error("Failed to parse multipart request", e); throw new ServletException(e); } // Partition the items into form fields and files. Iterator iter = items.iterator(); while (iter.hasNext()) { FileItem item = (FileItem) iter.next(); if (item.isFormField()) { addTextParameter(request, item); } else { addFileParameter(item); } } }从上述代码可以看到,首先定义DiskFileUpload对象,接着给这个对象设定参数,然后upload.parseRequest(request)返回解析后的参数列表(FileItem List) ,最后将结果放入相应的集合中。可见Struts调用fileupload主要用于解析header中的parameter,供自己后续处理。
相关推荐
ant编译时抛出OutOfMemoryError.doc
java虚拟机OutOfMemoryError:Java heap space堆dump文件,可以直接用来分析。
java.lang.OutOfMemoryError: Java heap space 解决方法
问题分析:java.lang.OutOfMemoryError unable to create new native thread - ado1986 - CSDN1
遭遇OutOfMemoryError 的解决方法。
java.lang.OutOfMemoryError,产生该错误的原因大都出于以下原因: JVM内存过小、程序不严密,产生了过多的垃圾.
Java内存分析工具MAT(Memory Analyzer Tool) 可以解析内存的消耗,定位具体的类,定位问题
java.lang.OutOfMemoryError处理错误
解决OutOfMemoryError: PermGen space,过程是痛苦的,结果是舒畅的
tomcat 出现 OutOfMemoryError 的解决方法
java.lang.OutOfMemoryError: PermGen space 解决方案
OutOfMemoryError_8种典型案例分享,定位java内存问题
报错 java.lang.OutOfMemoryError: PermGen space 报错 java.lang.OutOfMemoryError: Java heap 启动报错java.lang.ClassNotFoundException: 1catalina.org.apache.juli.FileHandler JAVA_OPTS="-server -Xms800m ...
Myeclipse下java.lang.OutOfMemoryError Java heap space的解决
java.lang.OutOfMemoryError: PermGen space
搜集整理关于java错误处理:java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space 资料整理
在本篇文章中,我们给大家分享了关于解决Java异常之OutOfMemoryError的问题的方法,有此需要的朋友们学习下。
java.lang.OutOfMemoryError: Java heap space 解决方法
内存不足OOM java.lang.OutOfMemoryError.