首页 > 解决方案 > 解析要发送到 ftp 的 pdf 图像时内存泄漏/峰值

问题描述

我有一个 20MB 和 9 页的 PDF 文档,我正在尝试将其转换为图像并将它们 FTP,我正在使用 pdfBox(2.0.16 版本),下面的逻辑导致堆内存从小于 500 MB 2000MB的方式。我遵循了 pdf 框的常见问题解答(https://pdfbox.apache.org/2.0/faq.html#outofmemoryrrror),但到目前为止它没有帮助。我从当前的 2.0.16 版本升级到 2.0.20,但这也没有解决问题。任何有关错误的指示都将对我解决此问题非常有帮助。20 MB 文件的 1500 MB 内存增加很多,我正在通过 tomcat 运行我的应用程序,它使服务器崩溃。

PS:如果有其他我可以做的事情或更多细节,请告诉我。

代码:

                PDDocument document = PDDocument.load(sourceFile,
                    MemoryUsageSetting.setupTempFileOnly());
                PDFRenderer renderer = new PDFRenderer(document);
                PDPageTree pdPageTree =  document.getDocumentCatalog().getPages();
                int pageNumber = 1;
                strPdfFileName = strPdfFileName.replace(" ", "_"); 
                UploadFileBuilder objUploadFileBuilder;
                BufferedImage image;
                File outputFile;
                for (int i = 0; i < pdPageTree.getCount(); i++) {
                    strUploadFileName = new StringBuilder(strPdfFileName);
                    strUploadFileName.append("_").append(pageNumber).append(".png");
                    image = renderer.renderImage(pageNumber-1);
                    outputFile = new File(strPdfFilePath + File.separator + strPdfFileName +"_"+ pageNumber +".png");
                    LOGGER.debug("Image Created ----> {}", outputFile.getName());
                    ImageIO.write(image, "png", outputFile);
                    image.flush();
                    objUploadFileBuilder=new UploadFileBuilder();
                    objUploadFileBuilder.on(strUploadFileName.toString()).path(strFtpDir.toString()).withData(outputFile.getAbsolutePath());
                    lstUploadFileBuilder.add(objUploadFileBuilder);
                    strFileNameList.add(strUploadFileName.toString());
                    pageNumber++;
                }


                document.close();
                UploadFileBuilder.execute(lstUploadFileBuilder);

在此处输入图像描述

Edit1:问题的堆栈跟踪,还进行了更改以刷新图像。

Eclipse 内存分析器

Edit2:当前 -XMX 设置(JVM 的最大内存分配池为 512m),我使用的是 java 1.8.0_91

标签: tomcatmemory-leaksout-of-memorypdfboxheap-memory

解决方案


您可以尝试以下方法:

(1) 将以下依赖添加到您的maven项目中(如果您不使用maven,则手动添加)

<dependency>
    <groupId>net.gredler</groupId>
    <artifactId>jdk9-png-writer-backport</artifactId>
    <version>1.0.0</version>
</dependency>

(2) 更改您的转换代码以使用 backportedWriter 和 FileCacheImageOutputStream:

private static void convertMethod2(File pdf) {
        try (final PDDocument document = PDDocument.load(pdf)) {
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            List<String> base64DocumentPages = new ArrayList<>();               

            for (int page = 0; page < document.getNumberOfPages(); ++page) {
                BufferedImage bim = pdfRenderer.renderImage(page);
                
                FileOutputStream baos = new FileOutputStream(strPdfFilePath + File.separator + strPdfFileName +"_"+ pageNumber +".png"););
                PNGImageWriterBackport writer = chosePngWriter();
            
                if(writer!=null) {
                    try (ImageOutputStream stream = new FileCacheImageOutputStream(baos,null)) {
                        writer.setOutput(stream);
                        writer.write(null,new IIOImage(bim, null, null), getImageParams(writer));
                    }
                    finally {
                        writer.dispose();
                    }
                }
                else {
                    System.err.println("PNGImageWriterBackport not found! Aborting");
                }
                //do what you need to do
            }
            document.close();
        }
        catch (IOException e) {
            //handle exception
        }
    }
    
    private static PNGImageWriterBackport chosePngWriter() {
        Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByFormatName("png");
        
        ImageWriter writer = null;
        while(imageWriters.hasNext()) {
        
            writer = imageWriters.next();
            if (writer instanceof PNGImageWriterBackport) {
                return (PNGImageWriterBackport)writer;
            }
        }
        return null;
    }
    
    private static ImageWriteParam getImageParams(PNGImageWriterBackport writer) {
        ImageWriteParam writeParam = writer.getDefaultWriteParam();
        //set compression mode which wasn't possible before
        writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        //0.0f highest compression, slowest
        //1.0f lowest compression, fastest
        writeParam.setCompressionQuality(0.86f);
        
        return writeParam;
    }

推荐阅读