Jasper сообщает об ошибке OutOfMemory при экспорте - PullRequest
3 голосов
/ 26 сентября 2011

Я написал веб-приложение для управления и запуска отчетов 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 для выполнения отчетов заставляет меня надеяться, что будет что-то подобное для экспорта. Я хочу испачкать руки и расширить некоторые внутренние классы Джаспера, но по понятным причинам идеальным решением было бы то, что было предоставлено самим Джаспером.

Ответы [ 2 ]

4 голосов
/ 28 сентября 2011

С момента публикации этого вопроса я также подал запрос на функцию в JasperSoft.В качестве продолжения я указал на метод JRVirtualizationHelper.setThreadVirtualizer.Этот метод позволяет вам установить JRVirtualizer, связанный с текущим потоком, который будет использоваться во время десериализации JasperPrint.

Я протестировал это в своем проекте с удовлетворительными результатами.Кажется, что функция, на которую я надеялся, действительно существует, хотя ее видимость в API может быть улучшена.

Пример кода:

JRVirtualizer virtualizer = new JRSwapFileVirtualizer(1000, new JRSwapFile(reportFilePath, 2048, 1024), true);
JRVirtualizationHelper.setThreadVirtualizer(virtualizer);
2 голосов
/ 27 сентября 2011

Думаю, ваша проблема в том, что .jrprint - это сериализованный объект Java, который вы должны полностью десериализовать. Вам нужно как-то разбить его на небольшие файлы, а затем объединить результаты во время экспорта.

Мое предложение немного связано, но я думаю, что оно может сработать, по крайней мере, в некоторых случаях:

  1. Заполните ваш отчет, используя JRVirtualizer. Используйте методы, которые возвращают экземпляр JasperPrint, чтобы избежать сброса всего в огромный .jrprint.
  2. Выполнить внутренний экспорт, используя JRXmlExporter. Хитрость заключается в том, чтобы использовать соответствующие JRExportParameter s, чтобы сказать Jasper экспортировать каждую страницу отдельно (вы можете использовать ZipOutputStream в качестве контейнера, чтобы избежать каталогов с большим количеством файлов).
  3. Если вы хотите сделать свой реальный экспорт, используйте JASPER_PRINT_LIST. Важно, чтобы реализация списка была lazy и создавала JasperPrint экземпляров один за другим, используя JRPrintXmlLoader, поэтому вам не нужно загружать все сразу.

В любом случае, вы должны проверить исходный код Jasper, чтобы проверить, выполним ли этот подход.

...