Jasper 在导出时报告 OutOfMemoryError

Eri*_*ric 5 java jasper-reports out-of-memory export-to-csv

我编写了一个用于管理和运行 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();
Run Code Online (Sandbox Code Playgroud)

不幸的是,当我在我提到的大文件上尝试执行此操作时,我得到一个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)
    ...
Run Code Online (Sandbox Code Playgroud)

从浏览 Jasper 的一些内部结构来看,无论我如何尝试设置此导出(我也尝试过JASPER_PRINT直接加载和设置参数),最终都会有一个调用JRLoader.loadObject(...)尝试加载我的整个 500MB 报告进入内存(参见net.sf.jasperreports.engine.JRAbstractExporter.setInput())。

我的问题是,有没有一种方法可以解决这个问题,而不是只用内存来解决问题?500MB 是可行的,但它并不能让我的应用程序面向未来,而且JRVirtualizer报告执行的解决方案让我希望有类似的导出功能。我愿意亲自动手扩展一些 Jasper 内部类,但出于显而易见的原因,理想的解决方案将是 Jasper 本身提供的解决方案。

Eri*_*ric 5

自从发布这个问题以来,我还向 JasperSoft 提交了功能请求。作为后续行动,有人向我指出了该JRVirtualizationHelper.setThreadVirtualizer方法。此方法允许您设置与当前线程关联的 JRVirtualizer,该 JRVirtualizer 将在 JasperPrint 反序列化期间使用。

我在我的项目中对此进行了测试,结果令人满意。看来我希望存在的功能确实存在,尽管它在 API 中的可见性可能会得到提高。

代码示例:

JRVirtualizer virtualizer = new JRSwapFileVirtualizer(1000, new JRSwapFile(reportFilePath, 2048, 1024), true);
JRVirtualizationHelper.setThreadVirtualizer(virtualizer);
Run Code Online (Sandbox Code Playgroud)

  • 是否需要调用clearThreadVirtualizer(),或者它会在某处自动清理吗? (3认同)