游乐游手机版
首页/AI教程/文章详情

Itext5高并发大数据量导出PDF内存溢出解决方案

时间:2026-08-15 13:51
这个问题困扰了我整整两个星期!! 关键字 itext5 outofmemory 内存溢出 大数据 高并发 多线程 pdf 导出 报表 itext 并发 在阅读

这个问题困扰了我整整两个星期!!

关键字 itext5 outofmemory 内存溢出 大数据 高并发 多线程 pdf 导出 报表 itext 并发

在阅读<4.3节(Dealing with large tables)时,书里提到:iText5 的 PdfPTable 实现了 ILargeElement 接口,只要我们手动设置 datatable.setComplete(false);,它就能够自动把表格内容持续写入 document 中。但是,问题偏偏就出在这里!!我们的 cell 数量非常多,多到它根本来不及及时写入。比如并发 100 个线程同时访问时,别说 tomcat 扛不住,就连 was 也一样会被拖垮,哪怕给它 8G 内存,照样会被吃满,最终服务卡死。这样的 PDF 导出方案,如果直接上线,谁能放心?

于是我开始疯狂 Google、百度、CSDN、JavaEye,到处查 iText5 内存溢出、PDF 导出高并发、多线程报表生成 的解决方案。说多了都是泪,最后几乎什么办法都试过了:清空 list、把指针赋 null、优化程序循环、优化 BufferedOutputStream 输出、优化下载流程,差点都想给 iText 作者写信了!!不过最终还是把这个高并发 PDF 导出的问题解决了:实现了边生成边输出到 IO 磁盘,避免了之前高并发场景下线程之间争抢内存和 IO 资源,导致死锁、卡死甚至服务器崩溃的问题。

后来无名小卒兄居然也遇到了同样的问题,而且这个博客我都不知道自己当时是怎么搜到的。他在处理过程中用了一个自己写的方法:根据行号来控制执行次数。比如可以定义为每 1000 行释放一次 table,先把 table 放到 document 中,因为这时 document 里已经能识别到你的 table 了,再通过 datatable.setComplete(false) 方法,让它持续把数据写入文件。等这一批数据写完之后,再把已经写进去的元素删除,也就是使用 table.deleteBodyRows();。这样一来,iText5 大数据表格导出时的内存压力确实能明显缓解,不过同时又会引出一个新的问题:每隔 1000 行就会重新生成一次表头。于是无名兄又加上了 table.setSkipFirstHeader(true);,到这里,前面几个关键问题基本都解决了。

 int _MAX_ROWS = 1000;//最大行数,之后清理 int row_count = 0;//初始值 if (  row_count % _MAX_ROWS == 0) {System.out.println("row_count % _MAX_ROWS="  row_count % _MAX_ROWS);// add table to Document document.add(datatable); // delete _MAX_ROWS from table to free memory datatable.deleteBodyRows();// let iText manage when table header written //datatable.setSkipFirstHeader(true);
}

或者

int fregmentSize = 1000;// 释放内存的行号int k = 0;for (int i = 0, h = bodys.size(); i < h; i  ) { if (i != 0 && i % fregmentSize == fregmentSize - 1) {System.out.println("第[ "   (i   1)   " ]行进行内存释放 "   ((k  )   1)   " th");document.add(datatable);datatable.deleteBodyRows();datatable.setSkipFirstHeader(true);} //.... 表格处理}

经过我的实际调试,我发现还有一个非常容易踩坑的问题:如果前一页底部剩余空间已经不足以放下一整行数据,那么写入时就必须自动换到下一页;这时候如果你强制使用 splitRow(false) 来禁止拆分行,就很可能导致数据丢失。所以更建议不要刻意禁止表格拆分。当然,如果你确实担心 iText PDF 导出过程中的内存占用,也可以适当把清理 table 的阈值调大一些,例如累计到 1000 行再清理,而不是 100 行就清一次,否则表格数据还没积累起来,内存与分页压力反而更明显。像下面这种处理方式就是我更推荐的做法:允许表格内容在上一页放一部分,下一页继续接着放即可。其实这种极限情况并不常见。比如下面这个 demo,就是我在 100 个线程并发访问、生成 5800 行 * 33 列数据时做出来的一个比较特殊的 PDF,前一页保留一点、后一页承接一点,打印出来反而更美观,完全没必要为了整行不拆分,硬让前一页留下大块空白。

1.清理缓存并换页,第200行数据被放到了下一页,199行之后出现了明显空白。(不推荐)

\

2.清理缓存但不强制换页(推荐)事实证明,当你把 fregmentSize 的值提升到每 1000 行清理一次时,通常就不会再出现这种分页空白的问题。具体设置多大,大家可以根据服务器内存和并发量自行权衡。

\

落雨(感谢无名兄提供的这个非常实用的思路!!先 add,再 delete,再继续 add,确实有效。good)

394263788

2013年9月11日 10:04:12

来源:https://developer.aliyun.com/article/255827
上一篇Flutter RUM监控实践:还原AI应用等待全过程 下一篇GEO是什么?生成式引擎优化与AI可见度提升方案
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
CAD零基础入门教程:坐标输入、图层管理与基础绘图命令
AI教程 · 2026-09-01

CAD零基础入门教程:坐标输入、图层管理与基础绘图命令

本文面向CAD零基础学习者,系统讲解坐标输入、图层管理与基础绘图命令的核心用法。通过分步实操与常见问题排查,帮助新手建立精确绘图习惯,掌握规范出图的基础能力。

CAD从入门到项目交付:绘图、标注、图块与实战工作流
AI教程 · 2026-09-01

CAD从入门到项目交付:绘图、标注、图块与实战工作流

掌握CAD的核心在于建立“画得准、标得清、复用快、交付稳”的工作流。本文提供从环境设置、高频命令组合、标注规范、图块标准化到项目分阶段交付的完整路径,帮助初学者避免常见返工陷阱,独立完成可检查、可复用、可打印的工程图纸。

Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤
AI教程 · 2026-09-01

Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤

本文详细解析 Claude Code 登录前的账号类型区分方法,涵盖个人订阅、Teams 席位与企业 Enterprise 席位的授权路径差异。提供终端登录命令、环境变量排查及常见异常处理步骤,帮助用户快速完成正确授权并避免登录路径混淆。

Claude Code 文件修改前的权限模式配置与命令审批指南
AI教程 · 2026-09-01

Claude Code 文件修改前的权限模式配置与命令审批指南

本文详细介绍Claude Code在修改文件前的权限模式配置方法,包括defaultMode可选值、permissions allow与deny规则设置、多层级配置文件管理以及 status验证技巧,帮助开发者安全高效地使用AI编程助手。

Claude Code接入VS Code后先测扩展和终端命令
AI教程 · 2026-09-01

Claude Code接入VS Code后先测扩展和终端命令

在VS Code中接入Claude Code后,建议优先验证扩展面板与集成终端两条入口。本文提供标准检查顺序、关键命令与常见故障排查路径,帮助你快速确认环境就绪,避免后续开发受阻。