You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-users@xmlgraphics.apache.org by "Spasojevic, Daniel" <Sp...@AUSTRALIA.Stortek.com> on 2003/06/16 02:01:20 UTC

Object retention when using removeChild

Hi,

Firstly, I think that the Batik SVG toolkit is very good, and I have been
very impressed with the overall quality and number of features implemented.

I am having trouble removing nodes from a SVG document that has been
associated with a JSVGCanvas object. The problem is that although calling
removeChild() stops the nodes from being displayed, it seems that the
objects associated with that node are retained - preventing the removed
nodes from being garbage collected. I am using Batik 1.5, the problem
appears when using 1.4.1_02 and 1.4.1_03 of Java, and under Windows 2000 and
KDE on linux.

Hopefully this is due to a coding error on my part.

There is a similiar message in the user archives at
http://koala.ilog.fr/batik/mlists/batik-users/archives/msg02214.html.
Although the message was replied to, I couldn't see anything to help solve
my problem.

I wrote a simple test program that built a SVG document that included a text
element, associated it with a JSVGCanvas, then removed all child nodes of
the document root. Using JProfiler, I found the following references to
associated SVGOMTextElement (the format of this output is: name of the
referant, number of references, total size of references):

--- Incoming references before removeChild()

class array content	6	784
field e of org.apache.batik.bridge.SVGTextElementBridge	1	40
field key of java.util.HashMap$Entry			1	24	
field lastChild of org.apache.batik.dom.AbstractParentNode$ChildNodes	1
24
field nextSibling of org.apache.dom.svg.SVGOMRectElement		1
56
field ownerElement of org.apache.batik.dom.GenericAttr			2
80
field parentNode of org.apache.batik.dom.GenericText			1
40
field target of org.apache.batik.bridge.BridgeContext$EventListenerMememto
2	48
field target of org.apache.batik.dom.event.DOMMutationEvent
2	112
field this$0 of org.apache.batik.dom.AbstractParentNode$ChildNodes
1	24
field this$0 of
org.apache.batik.dom.AbstractParentNode$ExtendedNamedNodeHashMap	1
24
field this$0 of
org.apache.batik.dom.AbstractParentNode$ExtendedNamedNodeHashMap	1
24
field value of java.util.HashTable$Entry
1	24
field value of java.util.HashTable$Entry
1	24

--- Incoming references after removeChild()

class array content	6	784
field e of org.apache.batik.bridge.SVGTextElementBridge	1	40
field ownerElement of org.apache.batik.dom.GenericAttr	2	80
field parentNode of org.apache.batik.dom.GenericText	1	40
field target of org.apache.batik.bridge.BridgeContext$EventListenerMememto
2	48
field this$0 of org.apache.batik.dom.AbstractParentNode$ChildNodes
1	24
field this$0 of
org.apache.batik.dom.AbstractParentNode$ExtendedNamedNodeHashMap	1
24
field this$0 of
org.apache.batik.dom.AbstractParentNode$ExtendedNamedNodeHashMap	1
24
field value of java.util.HashTable$Entry
1	24

These references are preventing the object from being garbage collected.
After many additions and removals during the execution of my application,
this causes a significant increase in the memory used.

I have included the source code to the test program below. It is really just
an amateurish mish-mash of the DOM API and JSVGCanvas tutorials. 

Any help as to how to prevent this object retention would be very much
appreciated.

-Daniel Spasojevic

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;

import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.w3c.dom.DOMImplementation;

import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.svg.JSVGComponent;

import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
import org.apache.batik.swing.svg.SVGDocumentLoaderAdapter;
import org.apache.batik.swing.svg.SVGDocumentLoaderEvent;
import org.apache.batik.swing.svg.GVTTreeBuilderAdapter;
import org.apache.batik.swing.svg.GVTTreeBuilderEvent;
import org.apache.batik.swing.svg.SVGLoadEventDispatcherAdapter;
import org.apache.batik.swing.svg.SVGLoadEventDispatcherEvent;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGDocument;


public class SVGApplication {

    public static void main(String[] args) {
        JFrame f = new JFrame("Batik");
        SVGApplication app = new SVGApplication(f);
        f.getContentPane().add(app.createComponents());

        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        f.setSize(400, 400);
        f.setVisible(true);

	app.buildDoc();
    }
    
    JFrame frame;
    JButton button = new JButton("Load...");
    JLabel label = new JLabel();
    JSVGCanvas svgCanvas = new JSVGCanvas();
    Document svgDocument;
    Element topLevelElt;

    DOMImplementation sImpl;
    String svgNS;

    Thread processUserThread;

    public SVGApplication(JFrame f) {
        frame = f;
    }

    public JComponent createComponents() {
        final JPanel panel = new JPanel(new BorderLayout());

        JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
        p.add(button);
        p.add(label);

        panel.add("North", p);
        panel.add("Center", svgCanvas);

        // Set the button action.
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                JFileChooser fc = new JFileChooser(".");
                int choice = fc.showOpenDialog(panel);
                if (choice == JFileChooser.APPROVE_OPTION) {
                    File f = fc.getSelectedFile();
                    try {
                        svgCanvas.setURI(f.toURL().toString());
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });

	svgCanvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);

        // Set the JSVGCanvas listeners.
        svgCanvas.addSVGDocumentLoaderListener(new
SVGDocumentLoaderAdapter() {
            public void documentLoadingStarted(SVGDocumentLoaderEvent e) {
                label.setText("Document Loading...");
            }
            public void documentLoadingCompleted(SVGDocumentLoaderEvent e) {
                label.setText("Document Loaded.");
            }
        });

        svgCanvas.addGVTTreeBuilderListener(new GVTTreeBuilderAdapter() {
            public void gvtBuildStarted(GVTTreeBuilderEvent e) {
                label.setText("Build Started...");
            }
            public void gvtBuildCompleted(GVTTreeBuilderEvent e) {
                label.setText("Build Done.");
                frame.pack();
            }
        });

        svgCanvas.addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
            public void gvtRenderingPrepare(GVTTreeRendererEvent e) {
                label.setText("Rendering Started...");
            }
            public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
                label.setText("Render complete");
		processUserThread = new Thread(new Runnable(){
			    public void run(){
				System.err.println("Second thread");
				doUserLoop();
			    }
		    });
		processUserThread.start();
		
	    }
        });

	svgCanvas.addSVGLoadEventDispatcherListener(new
SVGLoadEventDispatcherAdapter(){
		public void
svgLoadEventDispatchStarted(SVGLoadEventDispatcherEvent e){
		    label.setText("load event dispatch started");
		}
	    });

	sImpl = SVGDOMImplementation.getDOMImplementation();
	svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;

	
       
	

        return panel;
    }
    
    public void buildDoc(){
	svgDocument = (SVGDocument)sImpl.createDocument(svgNS, "svg", null);

	topLevelElt = svgDocument.getDocumentElement();
	topLevelElt.setAttributeNS(null, "width", "600");
	topLevelElt.setAttributeNS(null, "height", "450");

	// create the rectangle
	Element rectangle = svgDocument.createElementNS(svgNS, "rect");
	rectangle.setAttributeNS(null, "x", "10");
	rectangle.setAttributeNS(null, "y", "20");
	rectangle.setAttributeNS(null, "width", "100");
	rectangle.setAttributeNS(null, "height", "50");
	rectangle.setAttributeNS(null, "style", "fill:red");
	
	// attach the rectangle to the svg root element
	topLevelElt.appendChild(rectangle);
	

	svgCanvas.setSVGDocument((SVGDocument)svgDocument);
    }
    public void doUserLoop(){
	BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in));
	
	System.err.println("do user loop");
	
	try{
	    addSomeText();
	    while(true){
		
		String res = stdin.readLine();
		if(res.equals("c")){
		    clearGraph();
		} else {
		    addSomeText();
		}
	    }

	} catch (IOException ioe){
	    System.err.println("IOException");
	}
    }

    private void clearGraph(){
	
svgCanvas.getUpdateManager().getUpdateRunnableQueue().invokeLater(new
Runnable(){
		public void run(){

		    NodeList children = topLevelElt.getChildNodes();
		    if(children != null){
			int numC = children.getLength();
			System.err.println("num children: " + numC);
			for(int i = 0; i < numC; i++){
			    System.err.println(i);
			    System.err.println("removing: " +
children.item(0).getNodeName());
			    topLevelElt.removeChild(children.item(0));
			    System.err.println("removed");
			}
		    }
		    	}
	    });
    }

    private void addSomeText(){
		
	
svgCanvas.getUpdateManager().getUpdateRunnableQueue().invokeLater(new
Runnable(){
		    public void run(){
			System.err.println("Adding text");

			Element newTextElt =
svgDocument.createElementNS(svgNS, "text");
			newTextElt.setAttributeNS(null, "x", "300");
			newTextElt.setAttributeNS(null, "y", "200");
			
	
newTextElt.appendChild(svgDocument.createTextNode("Hello monkeys"));
			
			System.err.println("Appending");
			topLevelElt.appendChild(newTextElt);
			System.err.println("Appended");
			System.err.println("Added text");
		    }
		});
    }
}



---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-users-help@xml.apache.org


Re: Object retention when using removeChild

Posted by Thomas DeWeese <Th...@Kodak.com>.
Hi Daniel.

Spasojevic, Daniel wrote:

>Firstly, I think that the Batik SVG toolkit is very good, and I have been very impressed with the overall quality and number of features implemented.
>
    Thanks.

>
>I am having trouble removing nodes from a SVG document that has been
>associated with a JSVGCanvas object. The problem is that although calling removeChild() stops the nodes from being displayed, it seems that the objects associated with that node are retained - preventing the removed nodes from being garbage collected. I am using Batik 1.5, the problem appears when using 1.4.1_02 and 1.4.1_03 of Java, and under Windows 2000 and KDE on linux.
>
    Which Batik 1.5? Beta 5?

>Hopefully this is due to a coding error on my part.
>  
>
    One can always hope :)  However I think/know there is a memory leak 
here, I have found
and fixed at least one based on your information already.

>I wrote a simple test program that built a SVG document that included a text element, associated it with a JSVGCanvas, then removed all child nodes of the document root. Using JProfiler, I found the following references to associated SVGOMTextElement (the format of this output is: name of the referant, number of references, total size of references):
>  
>
    Thanks for taking the time to do this.  Good tests help a _lot_ I 
unfortuantely do not
have access to JProfiler.  Are you up for working through this with 
diffs etc?

>--- Incoming references after removeChild()
>  
>
I have categorized these:

Known (or strongly suspected) safe:

>field e of org.apache.batik.bridge.SVGTextElementBridge	1	40 
>field ownerElement of org.apache.batik.dom.GenericAttr	2	80
>field parentNode of org.apache.batik.dom.GenericText	1	40
>field target of field this$0 of org.apache.batik.dom.AbstractParentNode$ChildNodes
>1	24
>field this$0 of
>org.apache.batik.dom.AbstractParentNode$ExtendedNamedNodeHashMap	1 24
>field this$0 of
>org.apache.batik.dom.AbstractParentNode$ExtendedNamedNodeHashMap	1 24
>  
>
Now a known problem:

>org.apache.batik.bridge.BridgeContext$EventListenerMememto
>2	48
>  
>
    This class is now using SoftReferences in my local copy so it will 
not keep the
object in memory.  This change did not effect proper function, I still 
need to add a
ReferenceQueue so we don't accumulate Mememto's.  This will probably be
committed to CVS tonight.

Unknown source:

>class array content	6	784
>field value of java.util.HashTable$Entry
>1	24
>  
>
    I am especially baffled by the array references.  The HashTable may 
(or may
or may not be) harmless.

>These references are preventing the object from being garbage collected.
>After many additions and removals during the execution of my application,
>this causes a significant increase in the memory used.
>  
>




---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-users-help@xml.apache.org