Как исправить ошибку java.lang.OutOfMemoryError: пространство кучи Java при создании документа PDF? - PullRequest
0 голосов
/ 04 июля 2019

Моя система выдает исключение: "java.lang.OutOfMemoryError: пространство кучи Java", когда обрабатывается огромный файл.Я понял, что StringWriter.toString () вызывает двойной размер в куче, так что это может вызвать проблему.Как я могу оптимизировать блок следующего кода, чтобы избежать Out Of Memory.

public byte[] generateFromFo(final StringWriter foString) {
        try {
            StringReader foReader = new StringReader(foString.toString());
            ByteArrayOutputStream pdfWriter = new ByteArrayOutputStream();
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, fopFactory.newFOUserAgent(),pdfWriter);
            TRANSFORMER_FACTORY.newTransformer().transform(new StreamSource(foReader), new SAXResult(fop.getDefaultHandler()));
            LOG.debug("Completed rendering PDF output!");
            return pdfWriter.toByteArray();
        } catch (Exception e) {
            LOG.error("Error while generating PDF from FO",e);
            throw new AuditReportExportServiceException(AuditErrorCode.INTERNAL_ERROR,"Could not generate PDF from XSL-FO");
        }
    }

Ответы [ 2 ]

2 голосов
/ 04 июля 2019

Использование байтов InputStream может уменьшить объем памяти для foString до коэффициента 2 (char = 2 байта).

ByteArrayOutputStream изменяет размер во время своего заполнения, поэтому добавление предполагаемой потребности ускоряет работу и можетпредотвратить слишком большое изменение размера.

        InputStream foReader = new ByteArrayInputStream(
                foString.toString().getBytes(StandardCharsets.UTF_8);
        foString.close();
        final int initialCapacity = 160 * 1024;
        ByteArrayOutputStream pdfWriter = new ByteArrayOutputStream(initialCapacity);
        Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, fopFactory.newFOUserAgent(),
            pdfWriter);
        TRANSFORMER_FACTORY.newTransformer().transform(new StreamSource(foReader),
            new SAXResult(fop.getDefaultHandler()));

Лучше всего было бы изменить API:

public void generateFromFo(final String foString, OutputStream pdfOut) { ... }

Это может сделать ByteArrayOutputStream лишним, и вы можете сразу перейти к файлу, URL или что-то еще.

У самого документа и сгенерированного PDF также есть проблемы:

  • размеры изображений (но помните о более высоком разрешении отпечатков)
  • некоторыеизображения могут быть хорошо векторизованы
  • повторяющиеся изображения, как в заголовке страницы, должны быть сохранены один раз
  • шрифты в идеале должны быть стандартными шрифтами, вторые лучшие встроенные подмножества (из используемых символов)
  • XML может быть неоптимальным, очень повторяющимся
1 голос
/ 04 июля 2019

В общем, у вас есть два основных варианта:

  1. Увеличение объема памяти, доступной для вашего процесса. Параметр -Xmx для Java установит эту конфигурацию. Вы можете передать, например, -Xmx8G запросить 8 ГБ памяти в 64-битной системе, если у вас так много. Документы здесь: http://docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html#nonstandard

  2. Измените свой код так, чтобы «поток» данных проходил небольшими порциями, вместо того, чтобы пытаться собрать весь файл в byte[] в памяти, как вы это сделали здесь. Вы могли бы изменить выход вашего преобразователя на FileOutputStream вместо ByteArrayOutputStream и вернуть File вместо byte[] в показанном коде? Или, в зависимости от того, что вы делаете с выходными данными этого метода, вы можете вернуть InputStream и позволить потребителю получать данные файла потоковым способом?

    Вам также может понадобиться изменить положение, чтобы входные данные этого метода использовались в потоковом режиме. Как это сделать, зависит от деталей создания StringWriter foString. Вам может потребоваться "передать" OutputStream в InputStream, чтобы сделать эту работу, см. https://docs.oracle.com/javase/7/docs/api/java/io/PipedInputStream.html

1 проще. 2, вероятно, лучше здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...