After reading the Sept 2003 thread about Memory Performance, leaks (and
how wonderful ADA is), I modified my test program that generates
3 PDF files. The program now sleeps 30 seconds, calls Driver.reset(),
nulls the reference and sleeps again. In JMP this plots a square wave
between that you can read on the attached graphs.

It is clear that there is a fair bit of memory freed by Driver.reset(). 

After thinking it over, I modified the same test to skip reset() and
just null the reference and issue System.gc().

This should be the same as letting it go out of scope (which happens
afterwards but this way I get the square wave on the graph).

Guess what ?

Attachment 2: footprint2.png has about 1Mb more heap in use!
And this is a very short test file with just one member name & address.
The test prints a letter, envelope and a renewal form for a non-profit
Gardening group.

The difference ... no call to Driver.reset() !!!

Why ? Does this suggest that there are finalizers (destructors) that are
not being called ? References set to null inside reset() should all
be unreachable when the reference to Driver goes out of scope.

This might explain problems people are reporting when 
generating multiple PDF files using FOP. Especially if their
programs don't lose references to instances of Driver.

Personally, I suspect there are a lot of logical memory leaks
inside FOP. A reset() at the end of using a Driver instance is a
catch-all way of releasing all of the logically leaked memory
allocated from inside Driver() (and therefore inside FOP).

This approach is of little help to the developer who builds an
application that dies of memory exhaustion in production. We will have
to fix the logical leaks inside FOP to improve the user experience.


<<attachment: footprint2.png>>

<<attachment: footprint.png>>


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

import org.apache.avalon.framework.logger.ConsoleLogger;
import org.apache.avalon.framework.logger.Logger;

import org.apache.fop.apps.Driver;
import org.apache.fop.apps.FOPException;

* Use JAXP 1.1 to apply two transformations and FOP to generate PDF output
* for the Friends of the Gardens (FOG) project for the MUN Botanical Garden
* Requires: 
* (i)  Java >= 1.4 to obtain the XML parser and XSLT processor - JAXP 1.1
* (ii) FOP >= 0.20.5, fop.jar and the associated batik.jar and avalon-cvs-20020806000.jar
* (iii) Input file: members.xml
* (iv) Transforms: letter.xsl,  letter2fo.xsl, 
*                  env.xsl,     env2fo.xsl, 
*                  renewal.xsl, renewal2fo.xsl
* Compile:
* javac -classpath .;fop.jar;avalon-framework-cvs-20020806.jar
* Execute:
* java -Xmx400000000 -classpath .;fop.jar;batik.jar;avalov-framework-cvs-20020806.jar SimpleJaxp
* Alternative:
* cocoon: pipelines like this:
	<map:match pattern="renewal">
	  <map:enerate src="members.xml"/>
	  <map:transform src="renewal.xsl" />
	  <map:transform src"renewal2fo.xsl" />
	  <map:serialize type="fo2pdf"/>

public class SimpleJaxp extends java.lang.Thread {
	public static void main(String[] args)
		throws javax.xml.transform.TransformerException {

		java.util.Calendar cal = java.util.Calendar.getInstance();
		long start = cal.getTimeInMillis();
		transformToPDF( "letter",  "members.xml", "letter.xsl",  "letter2fo.xsl"  );
		transformToPDF( "env",     "members.xml", "env.xsl",     "env2fo.xsl"     );
		transformToPDF( "renewal", "members.xml", "renewal.xsl", "renewal2fo.xsl" );	
		System.out.println( "Elapsed " 
		+ ((java.util.Calendar.getInstance().getTimeInMillis() - start + 500)/1000)
		+ " seconds." );
		try {
		catch (InterruptedException e ) {
		    System.err.println( "sleep() Interrupted." );
	public static void transformToPDF(
		String namePart,
		String xmlFileName,
		String xsltFileName1,
		String xsltFileName2
		throws javax.xml.transform.TransformerException {
		File xmlFile = new File( xmlFileName );
		File xsltFile = new File( xsltFileName1);
		File out1 = null;
		try {
			out1 = File.createTempFile( namePart, ".xml" );
		catch( IOException ioe ) {
			System.err.println( "Could not create temp file" );
		//*** First transformation ***
		Source xmlSource = new StreamSource(xmlFile);
		Source xsltSource = new StreamSource(xsltFile);

		Result result = new StreamResult(out1);

		TransformerFactory transFact = TransformerFactory.newInstance();

		Transformer trans = transFact.newTransformer(xsltSource);

		trans.transform(xmlSource, result );
		trans = null;
		//*** Second transformation ***
		File xsltFile2 = new File( xsltFileName2);
		Source xsltSource2 = new StreamSource(xsltFile2);
		Transformer trans2 = transFact.newTransformer( xsltSource2 );
		Source xmlSource2 = new StreamSource(out1);
		//*** Generate PDF file ***
		File pdfFile = new File( namePart + ".pdf" );
		Driver driver = new Driver();
		Logger logger = new ConsoleLogger( ConsoleLogger.LEVEL_INFO ); 
		//driver.setLogger( logger );
		driver.setRenderer( Driver.RENDER_PDF );

		try {
		    driver.setOutputStream( new pdfFile ) );

		    Result res = new SAXResult(driver.getContentHandler());
		    trans2.transform( xmlSource2, res );
		catch( IOException ioe ) {
		    System.err.println( "Could not create output PDF file" );
		catch(Exception e) {
		    System.err.println( "FOP Exception" + e );
		trans2 = null;

		// Plot a square wave in the JMP Heap graph window
		try {

			driver = null;

		catch (InterruptedException e ) {
		    System.err.println( "sleep() Interrupted." );

Reply via email to