当前位置: 首页 > news >正文

导出内存溢出案例分析

内存溢出排查与优化-CSDN博客

参考如上示例

分析如下代码

分页查询

    protected IBean[] beforedownload(String token, Selector selector, Class<? extends IBean> clazz) {AssetFaDisposalLineExportResDTO[] resDTOS = assetFaDisposalServiceAgent.queryDetailExport(token, selector);if (ArrayUtils.isEmpty(resDTOS)) {return new IBean[0];}List<AssetFaDisposalForExport> exportList = new ArrayList<>();for (AssetFaDisposalLineExportResDTO resDTO : resDTOS) {AssetFaDisposalForExport export = new AssetFaDisposalForExport();// 头数据export.setCompanyName(resDTO.getCompanyName());export.setCompanyCode(resDTO.getCompanyCode());export.setCode(resDTO.getCode());export.setExpenseDate(resDTO.getExpenseDate());export.setDataSourceNum(resDTO.getDataSourceNum());export.setDataSource(resDTO.getDataSource());export.setPreparedBy(resDTO.getPreparedBy());export.setStatus(resDTO.getStatus());// 行数据export.setAssetNumber(resDTO.getAssetNumber());export.setTagNumber(resDTO.getTagNumber());export.setAssetName(resDTO.getAssetName());export.setAssetCategory1(resDTO.getAssetCategory1());export.setAssetCategory2(resDTO.getAssetCategory2());export.setRetiredUnits(resDTO.getRetiredUnits());export.setRetiredDate(resDTO.getRetiredDate());export.setRetirementType(resDTO.getRetirementType());export.setRetirementCost(resDTO.getRetirementCost());export.setDisposalAmount(resDTO.getDisposalAmount());export.setComment(resDTO.getComment());exportList.add(export);// MOD BY SHENKE 20250529 ED}Comparator<AssetFaDisposalForExport> companyCodeDesc = Comparator.comparing(AssetFaDisposalForExport::getCompanyCode).reversed();Comparator<AssetFaDisposalForExport> expenseDateDesc = Comparator.comparing(AssetFaDisposalForExport::getExpenseDate).reversed();Comparator<AssetFaDisposalForExport> assetNumberDesc = Comparator.comparing(AssetFaDisposalForExport::getAssetNumber).reversed();Comparator<AssetFaDisposalForExport> retiredDateDesc = Comparator.comparing(AssetFaDisposalForExport::getRetiredDate).reversed();Comparator<AssetFaDisposalForExport> finalComparator = companyCodeDesc.thenComparing(expenseDateDesc).thenComparing(assetNumberDesc).thenComparing(retiredDateDesc);return exportList.stream().sorted(finalComparator).collect(Collectors.toList()).toArray(new AssetFaDisposalForExport[0]);}

导出代码

 while (true) {System.out.println(pageIndex);StopWatch stopWatch = new StopWatch();request.getData().setPageIndex(pageIndex++);request.getData().setPageSize(EXPORT_PAGESIZE);request.getData().removeOrderByField();request.getData().addOrderByField(new OrderByField("id"));stopWatch.start("查询数据" + request.getData().getPageIndex());IBean[] beans = beforedownload(request.getToken(), request.getData(), clazz);stopWatch.stop();try {stopWatch.start("写入数据" + request.getData().getPageIndex());tools.toExcel(beans, request.getParentCode(), ExcelTools.XLSXTYPE);stopWatch.stop();//greaterThanPageSizeFlag//处理 行合并到头导出if(StringUtils.isNotBlank(greaterThanPageSizeFlag)){if (beans == null || beans.length < EXPORT_PAGESIZE) {break;}}else{//beans.length != EXPORT_PAGESIZE// 处理 写sql语句查询,sql未加分页条件,只能查询一次if (beans == null || beans.length != EXPORT_PAGESIZE) {break;}}} catch (CommonException e) {throw e;} catch (Exception e) {}//System.out.println("结果" + request.getData().getPageIndex() + ":" + stopWatch.prettyPrint());}} finally {try {tools.write(response.getOutputStream());} catch (IOException e) {}tools.close();}

问题

1,

如上分页导出会内存溢出

2,

说明:正确分页每页数据1000条, List<AssetFaDisposalForExport> exportList = new ArrayList<>();list中对象什么时候回收

标准回收时机(理想情况)

  1. 方法执行完成后立即回收

    • beforedownload方法执行完毕时

    • 如果exportList没有被外部引用

    • 整个列表及其包含的1000个AssetFaDisposalForExport对象都将变为可回收状态

在您的分页导出场景中:

  • 每页的exportList及其对象:会在方法执行完成后变为可回收状态

  • 实际回收时间:由JVM的GC决定,通常在下次GC时回收

  • 真正释放内存的关键

    1. 确保没有外部长期引用返回的数据

3,

当前代码 IBean[] beans = beforedownload(request.getToken(), request.getData(), clazz); stopWatch.stop(); try { stopWatch.start("写入数据" + request.getData().getPageIndex()); tools.toExcel(beans, request.getParentCode(), ExcelTools.XLSXTYPE);,bean没有设置为null,会被回收吗

回收机制分析

  1. 方法局部变量的生命周期

    • beans是方法局部变量(假设在某个方法内部)

    • 当方法执行结束时,局部变量表会被清空

    • 变量引用的对象如果没有被其他位置引用,将变为可回收状态

  2. 没有显式置null时的回收

    • 即使没有beans = null,Java的垃圾回收器也能正确识别不再使用的对象

    • 方法执行完毕后,beans的栈帧引用会自动消失

在您的代码中:

  • 没有显式设置beans = null不会导致内存泄漏,因为:

    • 方法结束后局部变量自动失效

    • 除非被其他对象引用,否则会被GC回收

  • 真正需要关注的是

    • toExcel()方法是否内部缓存数据

    • 是否有其他静态引用持有beans中的数据

    • Excel库是否是内存友好的实现

4,

没有显式设置beans = null不会导致内存泄漏,因为: 方法结束后局部变量自动失效,方法结束指整个导出结束吗,while循环,需要整个导出完成,才结束吧

  1. 每次迭代都是独立的作用域

    • 每次循环迭代都会创建一个新的beans数组

    • 前一次迭代的beans引用会在迭代结束时失效

    • 实际回收取决于

      • Excel工具类是否缓存了数据

      • JVM的垃圾回收策略

  2. 关键时间点

    • 当一次循环迭代完成时

      • 当前迭代的beans变量已经超出作用域

      • 如果没有其他引用指向这个数组,它就成为可回收对象

    • 不需要等待整个while循环结束

总结

在您的分页导出代码中:

  • 不需要等待整个while循环结束:每次循环迭代完成后,当次的beans数组就可以被回收

  • 无需显式设置beans = null:现代JVM能很好地处理这种情况

5,回头分析内存溢出原因

内存溢出排查与优化-CSDN博客

http://www.xdnf.cn/news/1126135.html

相关文章:

  • 学习秒杀系统-实现秒杀功能(商品列表,商品详情,基本秒杀功能实现,订单详情)
  • JavaScript认识+JQuery的依赖引用
  • ethers.js-8-bigNmber和callstatic模拟
  • 2025年最新香港站群服务器租用价格参考
  • 探索阿里云ESA:开启边缘安全加速新时代
  • 基于Ruoyi和PostgreSQL的统一POI分类后台管理实战
  • 论文阅读:arxiv 2025 A Survey on Data Contamination for Large Language Models
  • 从12kW到800V,AI服务器电源架构变革下,功率器件如何解题?
  • redisson 设置了过期时间,会自动续期吗
  • 【网络安全】大型语言模型(LLMs)及其应用的红队演练指南
  • 经典排序算法之希尔排序
  • docker 方式gost代理搭建以及代理链实施
  • HTTP常见误区
  • 具身智能零碎知识点(六):VAE 核心解密:重参数化技巧(Reparameterization Trick)到底在干啥?
  • 第二章 OB 存储引擎高级技术
  • JavaScript进阶篇——第四章 解构赋值(完全版)
  • IT岗位任职资格体系及发展通道——研发岗位任职资格标准体系
  • 进程探秘:从 PCB 到 fork 的核心原理之旅
  • 从零开始的云计算生活——第三十二天,四面楚歌,HAProxy负载均衡
  • 测试tcpdump,分析tcp协议
  • JAVA学习笔记 使用notepad++开发JAVA-003
  • Bootstrap-HTML(七)Bootstrap在线图标的引用方法
  • SELinux 详细解析
  • 【安卓笔记】RxJava之flatMap的使用
  • python原生处理properties文件
  • 第十四章 Stream API
  • 【第二章自定义功能菜单_MenuItemAttribute_顶部菜单栏(本章进度1/7)】
  • 零售企业用户行为数据画像的授权边界界定:合规与风险防范
  • 16、鸿蒙Harmony Next开发:组件扩展
  • RAG实战指南 Day 16:向量数据库类型与选择指南