问题描述
在使用XEasyPdf
(XEasyPdf是优良的封装Apache PDFBox的工具)写出流时,发生概率性的提示报错:
COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?
对,没错是概率性的,排除代码问题给出下面的解决方案:
发生原因
在使用 Apache PDFBox 中的 PDDocument
进行操作时,如果你确定没有显式地关闭 PDDocument
,恰巧此时发生了java的垃圾回收机制
就会遇到以下问题:
- 在调用
PDDocument
的方法去创建页面PDPage
时,会执行COSStream.checkClosed()
方法去检查COSStream
是否关闭:
private void checkClosed() throws IOException {
if (this.randomAccess != null && this.randomAccess.isClosed()) {
throw new IOException("COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?");
}
}
- 但是如果此时发生了java 垃圾回收调用了
ScratchFileBuffer
的finalize()
方法,就会导致randomAccess.isClosed() == true
,导致异常抛出。
protected void finalize() throws Throwable {
try {
if (this.pageHandler != null && LOG.isDebugEnabled()) {
LOG.debug("ScratchFileBuffer not closed!");
}
this.close();
} finally {
super.finalize();
}
}
解决办法—调用 System.gc()(解决了但不推荐)
此时需要及时释放内存,调用 System.gc()
来提示垃圾回收器尽快执行垃圾回收。但是,这并不是一种推荐的做法,因为垃圾回收的时机是由 JVM 决定的,而不是由程序员决定的。最好的做法还是在代码中正确地管理资源,但是目前我的pdf文件构造设计涉及太多,目前只能通过此种方法解决。
解决了
原来是自己有一个流忘了及时关,及时关就不会发现gc
了。调用 不需要调用System.gc()