2013-04-18 1 views
1

Как я могу избежать FOP, чтобы потреблять растущее количество памяти, даже если страницы не содержат передовых ссылок и < page-sequence> блоки очень малы?FOP - как избежать большого потребления памяти при очень большом количестве последовательностей страниц?

Вот тест Java программа, которая питает ФОП с ручным FO, который просто повторяет снова и ту же самое базовую последовательность страниц:

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, как только последовательность страниц была закрыта. Это означает, что страницы (должны?) Не сохраняются в памяти. Но память постоянно растет и растет. С размером кучи размером 256 Мбайт генерация останавливается примерно на 150000 последовательностей страниц.

Почему это происходит?

+0

У меня есть только 3 страницы в моем PDF-документе, и я столкнулся с той же проблемой. Каждый пробег занимает около 10 МБ, который не освобождается. Результирующий PDF - это только 100-иш kB большой, мой FOP-шаблон имеет примерно 8k строк. Я попытался перефразировать FOP до последней версии, без разницы. –

ответ

0

Я подозреваю, что, несмотря на ваш звонок sleep, ваш производитель работает намного быстрее, чем ваш потребитель, и поток ваших каналов заполняет вашу память. Два способа, я могу придумать, чтобы исправить это:

Вариант 1 заключается в использовании BlockingQueue вместо потока с потоком.

Вариант 2 заключается в добавлении метода public boolean pipeIsFull() к Fo2Pdf, который возвращает значение true, если in.available() превышает, я не знаю, 2 МБ. Тогда ваш основной цикл будет спать на 500 мс или что угодно, если pipeIsFull() истинно.

Кроме того, способ уменьшить потребление памяти является

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

Я не знаю, насколько значительным влияние это будет (это будет уменьшить его на пару гб, но это, вероятно, арахис по сравнению к тому, что использует Fo2Pdf), но это не может повредить.