JAVA 通过浏览器下载大文件导致OOM

2023-11-12

背景:

Response 获取的对象为:ContentCachingResponseWrapper
页面点击下载文件,后台报错如下:

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space
  at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1076) ~[spring-webmvc-5.3.2.jar:5.3.2]
  at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961) ~[spring-webmvc-5.3.2.jar:5.3.2]
  at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.3.2.jar:5.3.2]
  at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) [spring-webmvc-5.3.2.jar:5.3.2]

demo代码如下:

public static void dowlondFile(String filePath, HttpServletResponse response, HttpServletRequest request) {
        File file = new File(filePath);
        String fileName = "TALENT_RES_download.zip";
        if (file.exists()) {
            dowlondFile(file,fileName,response);
        }
    }
    /**
     * 下载文件
     * @param file 要下载的文件名
     * @param fileName 下载下来的文件命名
     * @param response  (ContentCachingResponseWrapper 类型)
     */
    public static void dowlondFile(File file, String fileName, HttpServletResponse response){
        try(InputStream inputStream =new FileInputStream(file) ) {
            response.setCharacterEncoding("utf-8");
            response.setContentType("multipart/form-data");
            response.setHeader("Content-disposition",
                    "attachment; filename="+ java.net.URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
            int length;
         //  OutputStream os = response.getOutputStream();
            OutputStream os = ((ContentCachingResponseWrapper)response).getResponse().getOutputStream();
            byte[] b = new byte[1024];
            while ((length = inputStream.read(b)) > 0) {
                os.write(b, 0, length);
            }
            os.flush();
            os.close();
        }catch (IOException e){
            log.error("Execute dowlondFile error.",e);
        }
    }

分析:

首先打印堆栈:

	打印堆栈方式:
	1. VM 参数设置:-XX:+HeapDumpOnOutOfMemoryError
	    当出现OOM时候,会打印堆栈
	2.  jmap -dump:format=b,file=fileName.hprof pid
	   备注:pid 为要打印堆栈进程的进程id. 

通过MAT分析堆栈信息:

如下:(发现主要出问题的对象为:FastByteArrayOutputStream
在这里插入图片描述
点击Details
在这里插入图片描述
点击红色框里面的See stacktrace.
在这里插入图片描述

解决措施:

通过堆栈,发现问题出在下图:在这里插入图片描述这边HttpServletResponse 类型为ContentCachingResponseWrapper
使用FastByteArrayOutputStream读取数据到内存,要下载的文件大小 > 可用内存大小。
而 CoyoteOutputStream 是读取数据到OutputBuffer。若超过限制,则flush出去,然后buffer清零。
因此将 OutputStream os = response.getOutputStream()
改为:OutputStream os = ((ContentCachingResponseWrapper)response).getResponse().getOutputStream()
经测试,未出现OOM。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA 通过浏览器下载大文件导致OOM 的相关文章

随机推荐