生产大文件下载导致 OOM,顺藤摸瓜拿下 上周遇到了生产环境 OOM 的问题找了一番之后基本定位了是大文件下载导致的问题于是在网上搜罗了一番文章下面分享一篇优质的解决方案整个排查思路非常清晰小白可以直接对照着来排查。事故发生上周五下午运营人员反馈笔者所负责的后台系统从 14 点以后就卡卡的虽然页面能够正常加载但是一直处于数据加载中数据也提交不了怀疑笔者的系统有BUG当听到运营人员的反馈我的第一反应是这不可能啊这么简单的一个后台系统还能出事故处理流程摘除其中一台服务器用于保留现场其他服务器先重启保证系统可用。下载GC日志系统dump文件用于分析GC log分析系统启动参数JVM内存分配-Xmx4096m -Xms4096m -Xmn2560m观察日志可知系统每隔 40S 发生一次 Full GC耗时 200 毫秒回收以后系统老年代占用也不大才 15M但是新生代回收完还有 2 个 G。有点不可思议竟然不是老年代塞满了数据而是新生代塞满了数据。初步推测是新生代数据要晋升到老年代结果放不进去而引起的 Full GC。使用 MAT 对 Dump 文件进行分析通过总图可以看出来目前系统内存占用超过 2 个 G:点击 Histogram 进行进一步分析看出系统中占用最多的是byte[]点击List Objects进入income引用统计界面层层点开发现byte[]被 ResponseEntity 对象所引用且数量不小翻阅代码1在系统中找到唯一ResponseEntity有关的代码2这代码看似没什么问题啊这不是很正常的文件下载么我去看看用户下载了啥跑到目录文件查看一下下。img我的天用户下载的是一份2.4G的大文件代码中FileUtils.readFileToByteArray(file)的方式是把整个文件读取到内存再输出流里写入此时内存不够分配又塞不进老年代只能是 Full GC 了。3成功破案了用户下载了一份大文件文件先加载到内存才往外写抹泪。。。。解决方案使用FileSystemResourceGetMapping(/down) public ResponseEntity download(RequestParam(uri) String uri) throws IOException { File file new File(uri); if (!file.isFile()) { throw new ServiceException(文件不存在); } String filename FilenameUtils.getName(uri); HttpHeaders headers new HttpHeaders(); headers.add(Content-Disposition, attachment;filename URLEncoder.encode(filename, UTF-8)); HttpStatus status HttpStatus.OK; return new ResponseEntity(new FileSystemResource(file), headers, status); }使用缓存流边读边写GetMapping(/down) public void download(RequestParam(uri) String uri, HttpServletResponse response) throws IOException { File file new File(uri); if (!file.isFile()) { throw new ServiceException(文件不存在); } String filename FilenameUtils.getName(uri); response.setHeader(Content-Disposition, attachment;filename URLEncoder.encode(filename, UTF-8)); try (FileInputStream fileInputStream new FileInputStream(file); BufferedInputStream bufferedInputStream new BufferedInputStream(fileInputStream); BufferedOutputStream bufferedOutputStream new BufferedOutputStream(response.getOutputStream())) { FileCopyUtils.copy(bufferedInputStream, bufferedOutputStream); } finally { // 使用的是try-with-resources } }文件存储到 oss 或者是七牛云将文件存储到 oss 或者是七牛云绕过服务器下载