使用iTextPDF 7复制PDF页面时批量处理内存溢出问题排查
问题分析与修复方案
看起来你遇到的OutOfMemory异常,核心原因是大量PDF对象没有及时从内存刷新到磁盘,随着处理的文档数量增多,内存被持续占用最终耗尽。咱们来一步步拆解问题点和对应的修复方案:
核心内存泄漏根源
你的代码里有几个关键的资源释放遗漏:
1. 目标页面未执行刷新
每次创建的PdfPage page = pdfDest.addNewPage(PageSize.A4);在处理完后,没有调用page.flush();。在iText中,页面对象默认会暂存在内存里,直到整个文档关闭才会写入磁盘。当处理上百个文档、成百上千个页面时,这些堆积的页面对象会吃掉大量内存。
2. 目标文档未定期刷新
因为你的方法会被多次调用,用来给同一个目标PDF追加内容,如果不定期调用pdfDest.flush();,所有未写入磁盘的PDF对象都会一直留在内存中,内存占用只会越来越高。
3. 潜在的笔误问题
代码里sourcePage.copyAsFormXObject(pdfDestino);中的pdfDestino应该是pdfDest的笔误吧?如果这是实际代码里的错误,会导致额外的无效对象创建,进一步加重内存负担。
修复后的代码示例
try { // 提前创建字体,避免循环中重复创建浪费内存 PdfFont courierFont = PdfFontFactory.createFont(FontConstants.COURIER); for(int pageIndex = 1; pageIndex<=pdfSource.getNumberOfPages(); ++pageIndex) { PdfPage sourcePage = pdfSource.getPage(pageIndex); Rectangle sourceRect = sourcePage.getPageSizeWithRotation(); PdfPage page = pdfDest.addNewPage(PageSize.A4); PdfCanvas canvas = new PdfCanvas(page); // 计算缩放变换矩阵 AffineTransform transformationMatrix = AffineTransform.getScaleInstance( (page.getPageSize().getWidth() / sourcePage.getWidth()) * 0.95, (page.getPageSize().getHeight() / sourcePage.getHeight()) * 0.95); canvas.concatMatrix(transformationMatrix); try { // 修正笔误:pdfDestino -> pdfDest PdfFormXObject pageCopy = sourcePage.copyAsFormXObject(pdfDest); float x = (float)(page.getPageSize().getWidth()*0.05); float y = (float)(page.getPageSize().getHeight()*0.05); canvas.addXObject(pageCopy, x, y); pageCopy.flush(); // 刷新当前页面,释放内存中的页面对象 page.flush(); } catch(Exception e) { // 建议添加日志记录,方便后续排查问题 e.printStackTrace(); } // 重置变换矩阵准备添加页眉页脚 transformationMatrix = AffineTransform.getScaleInstance( sourceRect.getWidth() / page.getPageSize().getWidth(), sourceRect.getHeight() / page.getPageSize().getHeight() ); canvas.concatMatrix(transformationMatrix); canvas.setFillColorRgb(0.0f, 0.0f, 0.65f) .setFontAndSize(courierFont, 11); // 添加额外文本的逻辑... // 每处理20页就刷新一次目标文档,减少内存堆积 if (pageIndex % 20 == 0) { pdfDest.flush(); } } // 当前文档处理完成后,务必刷新目标文档 pdfDest.flush(); } finally { pdfSource.close(); }
额外优化小贴士
- 复用字体对象:原来的代码每次循环都创建新的Courier字体,这会产生大量重复的字体对象。现在把字体创建移到循环外,复用同一个对象,能节省不少内存。
- 改用try-with-resources:对于
PdfDocument这类可关闭资源,推荐用try-with-resources语法,它会自动帮你处理资源关闭,避免遗漏:try (PdfDocument pdfSource = new PdfDocument(new PdfReader("source.pdf"))) { // 循环处理页面的逻辑 } - 监控内存变化:可以加个日志,记录每次循环后的内存使用情况,方便确认内存泄漏是否真的解决了。
内容的提问来源于stack exchange,提问作者AlexSC
相关产品推荐
相关产品推荐

