问题描述
我编写了一个用于管理和运行Jasper报告的Web应用程序。最近我一直在处理一些生成极大(1500页以上)输出的报告,并尝试解决由此产生的内存问题。我发现了,这使我能够以非常有限的内存占用成功运行报告。但是,我的应用程序的一个功能是它存储以前运行的报告中的输出文件,并允许它们导出为各种格式(PDF,CSV等)。因此,我发现自己处于500 + MB .jrprint文件的状态,并希望将其导出为例如CSV按需。以下是一些简化的示例代码:
I have written a web app for managing and running Jasper reports. Lately I've been working with some reports that generate extremely large (1500+ page) outputs, and attempting to resolve the resultant memory issues. I have discovered the JRFileVirtualizer
, which has allowed me to run the report successfully with a very limited memory footprint. However, one of the features of my application is that it stores output files from previously run reports, and allows them to be exported to various formats (PDF, CSV, etc.). Therefore, I find myself in the situation of having a 500+MB .jrprint file and wanting to export it to, for example, CSV on demand. Here is some simplified example code:
JRCsvExporter exporter = new JRCsvExporter();
exporter.setParameter(JRExporterParameter.INPUT_FILE_NAME, jrprintPath);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);
exporter.exportReport();
不幸的是,当我在我提到的大文件上尝试这个时,我得到一个 OutOfMemoryError
:
Unfortunately, when I attempt this on the large file I mentioned, I get an 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( )
)。
From browsing some of the Jasper internals, it looks like no matter how I attempt to set up this export (I have also tried loading and setting the JASPER_PRINT
parameter directly), there will ultimately be a call to JRLoader.loadObject(...)
which will attempt to load my entire 500MB report into memory (see net.sf.jasperreports.engine.JRAbstractExporter.setInput()
).
我的问题是,有没有办法解决这个问题记忆中的问题? 500MB是可行的,但它不会让我的应用程序非常适合未来,而报告执行的 JRVirtualizer
解决方案让我希望出口会有类似的东西。我愿意弄脏并扩展一些Jasper内部类,但理想的解决方案是Jasper本身提供的解决方案,原因很明显。
My question is, is there a way around this that doesn't involve just throwing memory at the problem? 500MB is doable, but it doesn't leave my application very future-proof, and the JRVirtualizer
solution for report execution leaves me hoping there will be something similar for export. I am willing to get my hands dirty and extend some of the Jasper internal classes, but the ideal solution would be one provided by Jasper itself, for obvious reasons.
推荐答案
自发布此问题以来,我还提交了与JasperSoft。作为后续行动,我被指向方法。此方法允许您设置与当前线程关联的JRVirtualizer,该线程将在JasperPrint反序列化期间使用。
Since posting this question, I have also filed a feature request with JasperSoft. As a follow-up, I was pointed to the JRVirtualizationHelper.setThreadVirtualizer
method. This method allows you to set a JRVirtualizer associated with the current thread, which will be used during JasperPrint deserialization.
我已在我的项目中对此进行了测试,结果令人满意。似乎我希望存在的功能确实存在,尽管它在API中的可见性可能会得到改善。
I have tested this in my project with satisfactory results. It seems the feature I was hoping existed does indeed exist, although its visibility in the API could potentially be improved.
代码示例:
JRVirtualizer virtualizer = new JRSwapFileVirtualizer(1000, new JRSwapFile(reportFilePath, 2048, 1024), true);
JRVirtualizationHelper.setThreadVirtualizer(virtualizer);
这篇关于Jasper在导出时报告OutOfMemoryError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!