Я написал веб-приложение для управления и запуска отчетов Jasper. В последнее время я работал с некоторыми отчетами, которые генерируют очень большие (более 1500 страниц) выходные данные, и пытался решить возникающие проблемы с памятью. Я обнаружил JRFileVirtualizer
, который позволил мне успешно запустить отчет с очень ограниченным объемом памяти. Однако одной из особенностей моего приложения является то, что оно сохраняет выходные файлы из ранее выполненных отчетов и позволяет экспортировать их в различные форматы (PDF, CSV и т. Д.). Поэтому я нахожусь в ситуации, когда у меня есть файл .jrprint размером более 500 МБ, и я хочу экспортировать его, например, в 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(...)
который попытается загрузить весь мой отчет размером 500 МБ в память (см. net.sf.jasperreports.engine.JRAbstractExporter.setInput()
).
У меня вопрос, есть ли способ обойти это, который не включает просто бросание памяти на проблему? 500 МБ выполнимо, но это не оставляет мое приложение очень перспективным, а решение JRVirtualizer
для выполнения отчетов заставляет меня надеяться, что будет что-то подобное для экспорта. Я хочу испачкать руки и расширить некоторые внутренние классы Джаспера, но по понятным причинам идеальным решением было бы то, что было предоставлено самим Джаспером.