我的系统在处理一个巨大的文件时抛出异常:“ java.lang.OutOfMemoryError:Java堆空间”。我意识到StringWriter.toString()会导致堆中的大小增加一倍,因此可能会导致问题。我如何优化下面的代码块以避免内存不足。

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");
        }
    }

最佳答案

使用字节的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可能不是最佳选择,非常重复

09-16 10:21