2013-04-18 24 views
1

即使頁面不包含正向引用和頁面序列>塊非常小,我如何避免FOP消耗越來越多的內存?FOP - 如何避免大量頁面序列的高內存消耗?

這是一個測試Java程序,以取得FO手剛剛重複一遍又一遍同樣的非常基本的頁面序列飼料FOP:

Fo2Pdf.java

import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.OutputStream; 
import java.io.PipedInputStream; 

import javax.xml.transform.Result; 
import javax.xml.transform.Source; 
import javax.xml.transform.Transformer; 
import javax.xml.transform.TransformerFactory; 
import javax.xml.transform.sax.SAXResult; 
import javax.xml.transform.stream.StreamSource; 

import org.apache.fop.apps.FOUserAgent; 
import org.apache.fop.apps.Fop; 
import org.apache.fop.apps.FopFactory; 
import org.apache.fop.apps.MimeConstants; 
import org.xml.sax.helpers.DefaultHandler; 

public class Fo2Pdf implements Runnable { 

private PipedInputStream in; 

public Fo2Pdf(PipedInputStream in) { 
    this.in = in; 
} 


@Override 
public void run() { 
    // instantiate Fop factory 
    FopFactory fopFactory = FopFactory.newInstance(); 
    fopFactory.setStrictValidation(false); 

    // Setup output 
    OutputStream out = null; 
    try { 
     out = new FileOutputStream("output.pdf"); 
    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } 

    try { 
     // Setup user agent 
     FOUserAgent userAgent = fopFactory.newFOUserAgent(); 
     userAgent.setConserveMemoryPolicy(true); 

     Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out); 

     // Setup JAXP using identity transformer 
     TransformerFactory factory = TransformerFactory.newInstance(); 
     Transformer transformer = factory.newTransformer(); 

     // Setup input stream 
     Source src = new StreamSource(in); 

     // Resulting SAX events (the generated FO) must be piped through to FOP 
     DefaultHandler defaultHandler = (DefaultHandler) fop.getDefaultHandler(); 
     Result res = new SAXResult(defaultHandler); 

     // Start FOP processing 
     transformer.transform(src, res); 

    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 
} 

FeedFo.java

import java.io.IOException; 
import java.io.PipedInputStream; 
import java.io.PipedOutputStream; 


public class FeedFo { 


public static void main(String args[]) throws IOException, InterruptedException { 

    // instantiate and connect the pipes 
    PipedInputStream in = new PipedInputStream(); 
    PipedOutputStream out = new PipedOutputStream(in); 

    // Fo2Pdf - instantiate and start consuming the stream 
    Fo2Pdf fo2Pdf = new Fo2Pdf(in); 
    Thread fo2PdfThread = new Thread(fo2Pdf, "Fo2Pdf"); 
    fo2PdfThread.start(); 

    /* 
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> 
     <fo:layout-master-set> 
      <fo:simple-page-master master-name="A4" page-width="210mm" page-height="297mm"> 
       <fo:region-body/> 
      </fo:simple-page-master> 
     </fo:layout-master-set> 

    */ 
    out.write(("<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\"><fo:layout-master-set>" + 
      "<fo:simple-page-master master-name=\"A4\" page-width=\"210mm\" page-height=\"297mm\">" + 
      "<fo:region-body/></fo:simple-page-master></fo:layout-master-set>").getBytes()); 


    for(int i=0; i<100000000; i++) { 

     // sleep 3 seconds every 50000 page-sequences to make sure the consumer is faster than the producer 
     if(i % 50000 == 0) { 
      Thread.currentThread().sleep(3000); 
     } 

     /* 
     <fo:page-sequence xmlns:fo="http://www.w3.org/1999/XSL/Format" master-reference="A4"> 
      <fo:flow flow-name="xsl-region-body"> 
       <fo:block/> 
      </fo:flow> 
     </fo:page-sequence> 
     */ 
     out.write(("<fo:page-sequence xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" master-reference=\"A4\"><fo:flow flow-name=\"xsl-region-body\"><fo:block/></fo:flow></fo:page-sequence>").getBytes()); 
    } 

    out.write("</fo:root>".getBytes()); 
    out.flush(); 
    out.close(); 

    fo2PdfThread.join(); 

    System.out.println("Exit"); 
} 
} 

正如您所注意到的,FOP在頁面序列關閉後立即寫入PDF磁盤。這意味着頁面(應該?)不會被保存在內存中。但是,記憶力正在不斷增長和增長。 隨着256MB的堆大小,生成停止在約150000頁的序列。

這是怎麼發生的?

+0

我只有3頁在我的PDF和我面臨同樣的問題。每次運行需要另一個cca 10MB,不會被釋放。生成的PDF只有100-kB大,我的FOP模板壽有8K行。我嘗試將FOP上傳到最新版本,沒有任何區別。 –

回答

0

我懷疑,儘管撥打電話sleep,製片人的工作速度比消費者快得多,而您的管道流正在填滿您的記憶。有兩種方法可以解決此問題:

選項1是使用BlockingQueue而不是管道流。

選項2是將public boolean pipeIsFull()方法添加到Fo2Pdf,如果in.available()超過I dunno 2mb,則返回true。然後,如果pipeIsFull()爲真,那麼您的主for循環將睡眠500毫秒或其他值。

此外,爲減少你的內存消耗的方式是

byte[] bytes = ("<fo:page-sequence xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" master-reference=\"A4\"><fo:flow flow-name=\"xsl-region-body\"><fo:block/></fo:flow></fo:page-sequence>").getBytes(); 
for(int i=0; i<100000000; i++) { 
    ... 
    out.write(bytes); 
} 

我不知道的影響有顯著這將對(它會通過幾個GB的減少,但可能比花生這是到Fo2Pdf使用什麼),但它不能傷害。