使用PDFBox Splitter拆分多PDF后遇OutOfMemoryError问题咨询
我有一批PDF文件需要拆分为单页PDF。编写的脚本在处理单个或少量文件时正常,但处理总计6000页的多份PDF时,最终会出现内存不足,并打印提示Warning: You did not close a PDF Document。通过设置MemoryUsageSetting.setupMixed(50_000_000)降低内存配置后可运行,但设置为200MB时仍会内存不足。我认为PDFBox内部存在未关闭的COSDocument,这也是出现警告的原因。我确认已关闭所有创建的PDDocument,甚至编写代码检查所有可访问的实例,但相关日志并未输出。
以下是我的代码:
int splitPdf( File pdfFile ) { String filePrefix = prefix if( !filePrefix ) filePrefix = pdfFile.name[0..<(pdfFile.name.lastIndexOf("."))] logger.info("Splitting ${pdfFile.name} pages ${startAtPage} - ${endAtPage} every ${splitPagesEvery} pages") PDDocument document = PDDocument.load( pdfFile, MemoryUsageSetting.setupTempFileOnly() ) try { Splitter splitter = new Splitter() splitter.setStartPage(startAtPage) splitter.setEndPage(endAtPage) splitter.setSplitAtPage(splitPagesEvery) splitter.memoryUsageSetting = MemoryUsageSetting.setupMixed(50_000_000) int page = startAtPage > 0 ? startAtPage : 1 splitter.split(document).each { PDDocument doc -> String filename = "${filePrefix}-${page}.pdf" try { if( extractions ) { PDFTextStripper stripper = new PDFTextStripper() String pageText = stripper.getText( doc ) Map<String,String> results = extractions.collectEntries([filename: filename]) { name, spec -> [ name, spec.call(pageText) ] } this.manifest.write(results) } doc.save( new File( destDir, filename) ) page++ } finally { doc.close() if( !doc.document.isClosed() ) logger.info("${filename} is NOT closed!") } } return page - startAtPage } finally { document.close() if( !document.document.isClosed() ) logger.info("${pdfFile.name} is NOT closed!") } }
我暂无证据证明PDFBox 2.0.29存在内存泄漏,但无法解释为何会出现大量未关闭PDF文档的警告。我正准备编写脚本通过分析器查看COSDocument实例数量是否远超预期。我的问题是:在执行页面拆分时,PDFBox内部是否可能会创建未被释放的额外COSDocument?
在PDFBox的Splitter拆分流程中,确实存在内部创建额外COSDocument但未被及时释放的可能性,尤其是处理大文件或多页文档时:
Splitter内部实现的隐性实例
Splitter拆分时会为每个子文档创建新的COSDocument,但如果拆分过程中出现隐性异常(比如资源读取中断),部分临时COSDocument可能未关联到返回的PDDocument对象中,导致无法通过你代码里的doc.close()触发关闭。PDFTextStripper的残留资源
你使用PDFTextStripper提取文本时,该工具内部会生成临时解析对象,这些对象依赖文档的关闭时机回收资源。若文本提取过程中出现未捕获的隐性错误,可能导致部分COSDocument引用残留。内存配置与回收的延迟冲突
当设置MemoryUsageSetting.setupMixed时,PDFBox会在内存和临时文件间切换存储,但如果临时文件清理不及时,加上JVM垃圾回收延迟,会造成COSDocument实例堆积,触发内存不足和未关闭警告。排查方向建议
- 改用
Splitter.setSplitAtPage(1)直接拆分为单页,减少拆分过程中的中间文档实例 - 检查
manifest.write(results)是否存在IO阻塞,导致文档对象无法及时被回收 - 用分析器跟踪
COSDocument的创建/销毁路径,重点查看Splitter.split()方法内部的对象处理逻辑
- 改用
内容的提问来源于stack exchange,提问作者chubbsondubs

