我编写了一个用于管理和运行 Jasper 报告的 Web 应用程序。最近我一直在处理一些生成非常大(1500 多页)输出的报告,并试图解决由此产生的内存问题。我发现了 JRFileVirtualizer
,这让我能够以非常有限的内存占用成功运行报告。但是,我的应用程序的一个特性是它存储以前运行的报告的输出文件,并允许将它们导出为各种格式(PDF、CSV 等)。因此,我发现自己有一个 500+MB 的 .jrprint 文件,并希望按需将其导出为 CSV 等格式。下面是一些简化的示例代码:
JRCsvExporter exporter = new JRCsvExporter();
exporter.setParameter(JRExporterParameter.INPUT_FILE_NAME, jrprintPath);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);
exporter.exportReport();
不幸的是,当我在我提到的大文件上尝试这样做时,我得到了一个OutOfMemoryError
:
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.io.ObjectInputStream$HandleTable.grow(ObjectInputStream.java:3421)
at java.io.ObjectInputStream$HandleTable.assign(ObjectInputStream.java:3227)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1744)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at java.util.ArrayList.readObject(ArrayList.java:593)
at sun.reflect.GeneratedMethodAccessor184.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1849)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at net.sf.jasperreports.engine.base.JRVirtualPrintPage.readObject(JRVirtualPrintPage.java:423)
...
从浏览一些 Jasper 内部结构来看,无论我如何尝试设置这个导出(我也尝试过直接加载和设置 JASPER_PRINT
参数),最终都会有一个调用 JRLoader.loadObject(...)
,它将尝试将我的整个 500MB 报告加载到内存中(参见 net.sf.jasperreports.engine.JRAbstractExporter.setInput()
).
我的问题是,有没有解决这个问题的方法,而不是仅仅用内存来解决这个问题? 500MB 是可行的,但它并不能使我的应用程序非常适合 future ,而且用于报告执行的 JRVirtualizer
解决方案让我希望有类似的东西用于导出。我愿意亲自动手并扩展一些 Jasper 内部类,但理想的解决方案是 Jasper 本身提供的解决方案,原因很明显。
最佳答案
自发布此问题以来,我还提交了一个 feature request与 JasperSoft。作为后续行动,我被指向了 JRVirtualizationHelper.setThreadVirtualizer
方法。此方法允许您设置与当前线程关联的 JRVirtualizer,它将在 JasperPrint 反序列化期间使用。
我已经在我的项目中对此进行了测试,结果令人满意。看来我希望存在的功能确实存在,尽管它在 API 中的可见性可能会得到改善。
代码示例:
JRVirtualizer virtualizer = new JRSwapFileVirtualizer(1000, new JRSwapFile(reportFilePath, 2048, 1024), true);
JRVirtualizationHelper.setThreadVirtualizer(virtualizer);
关于java - Jasper 在导出时报告 OutOfMemoryError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7560499/