Lukasz Matuszczak
2005-02-17 12:59:09 UTC
Hello,
I am still fighting with "dispose()" problems, and I discovered something, which I consider a bug (it appears in 1.5.1 release and in a latest version from CVS head).
The problem is when I call method "suspendProcessing()" followed by "dispose()" on JSVGCanvas object,
the JSVGCanvas won't be collected by garbage collector. This is somehow connected with UpdateManager ant its RunnableQueue, I think.
You may ask why i call dispose() after suspendProcessing() (why not only dispose()). The answer is that I have my own queue of events in my application, and sometimes I get a request to deactivate the SVG screen (suspendProcessing), and soon after that to close it (close). I handle these events one after another (of course AWT in event dispatch thread).
The sample code is:
MemoryLeakTest.java:
-----------------------------------
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLConnection;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGDocument;
public class MemoryLeakTest extends JFrame {
JPanel left;
JPanel right = new JPanel();
Document doc = null;
JSVGCanvas canvas = null;
public MemoryLeakTest() {
right.setLayout(new BorderLayout());
this.getContentPane().setLayout(new BorderLayout());
this.setSize(new Dimension(1024, 768));
this.setTitle("MemoryTestLeak");
JButton button1 = new JButton("Start");
JButton button2 = new JButton("Stop");
JButton button3 = new JButton("GC");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start();
}
});
button2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stop();
}
});
button3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
gc();
}
});
left = new JPanel();
left.setLayout(new BoxLayout(left, BoxLayout.Y_AXIS));
left.add(button1);
left.add(button2);
left.add(button3);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(left, BorderLayout.WEST);
this.getContentPane().add(right, BorderLayout.CENTER);
start();
}
private void start() {
if (canvas == null) {
canvas = new JSVGCanvas();
final ReferenceQueue rq = new ReferenceQueue();
final WeakReference ref = new WeakReference(canvas, rq);
new Thread() {
public void run() {
try {
rq.remove();
System.out.println("CANVAS removed "+ref);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
.start();
try {
loadDocument();
} catch (Exception e) {
e.printStackTrace();
}
right.add(canvas, BorderLayout.CENTER);
pack();
}
}
private void stop() {
if (canvas!=null) {
canvas.suspendProcessing();
canvas.dispose();
canvas = null;
doc = null;
right.removeAll();
this.invalidate();
this.pack();
System.out.println(
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
}
}
private void gc() {
System.gc();
System.out.println(
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
}
private void loadDocument() throws Exception {
String stringURL = "file:circle.svg";
Reader reader = null;
SVGDocument svgDoc = null;
URL url = new URL(stringURL);
URLConnection connection;
connection = url.openConnection();
reader = new InputStreamReader(connection.getInputStream());
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
doc = f.createSVGDocument("file:circle.svg", reader);
canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);
canvas.setDoubleBufferedRendering(true);
canvas.setSize(new Dimension(800, 600));
canvas.setDocument(doc);
}
public static void main(String[] args) {
JFrame frame = new MemoryLeakTest();
frame.pack();
frame.setVisible(true);
}
}
-------------------------------
circle.svg:
---------------------------
<?xml version="1.0" encoding="windows-1250" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="12cm" height="4cm" viewBox="0 0 1200 400"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<desc>Example circle01 - circle filled with red and stroked with blue</desc>
<circle cx="600" cy="200" r="100"
fill="red" stroke="blue" stroke-width="10" />
</svg>
------------------------------
After pressing stop and then gc one should see "CANVAS removed ***@e2892b" message, but it appears hardly ever.
Regards,
Lukasz
I am still fighting with "dispose()" problems, and I discovered something, which I consider a bug (it appears in 1.5.1 release and in a latest version from CVS head).
The problem is when I call method "suspendProcessing()" followed by "dispose()" on JSVGCanvas object,
the JSVGCanvas won't be collected by garbage collector. This is somehow connected with UpdateManager ant its RunnableQueue, I think.
You may ask why i call dispose() after suspendProcessing() (why not only dispose()). The answer is that I have my own queue of events in my application, and sometimes I get a request to deactivate the SVG screen (suspendProcessing), and soon after that to close it (close). I handle these events one after another (of course AWT in event dispatch thread).
The sample code is:
MemoryLeakTest.java:
-----------------------------------
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLConnection;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGDocument;
public class MemoryLeakTest extends JFrame {
JPanel left;
JPanel right = new JPanel();
Document doc = null;
JSVGCanvas canvas = null;
public MemoryLeakTest() {
right.setLayout(new BorderLayout());
this.getContentPane().setLayout(new BorderLayout());
this.setSize(new Dimension(1024, 768));
this.setTitle("MemoryTestLeak");
JButton button1 = new JButton("Start");
JButton button2 = new JButton("Stop");
JButton button3 = new JButton("GC");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start();
}
});
button2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stop();
}
});
button3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
gc();
}
});
left = new JPanel();
left.setLayout(new BoxLayout(left, BoxLayout.Y_AXIS));
left.add(button1);
left.add(button2);
left.add(button3);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(left, BorderLayout.WEST);
this.getContentPane().add(right, BorderLayout.CENTER);
start();
}
private void start() {
if (canvas == null) {
canvas = new JSVGCanvas();
final ReferenceQueue rq = new ReferenceQueue();
final WeakReference ref = new WeakReference(canvas, rq);
new Thread() {
public void run() {
try {
rq.remove();
System.out.println("CANVAS removed "+ref);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
.start();
try {
loadDocument();
} catch (Exception e) {
e.printStackTrace();
}
right.add(canvas, BorderLayout.CENTER);
pack();
}
}
private void stop() {
if (canvas!=null) {
canvas.suspendProcessing();
canvas.dispose();
canvas = null;
doc = null;
right.removeAll();
this.invalidate();
this.pack();
System.out.println(
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
}
}
private void gc() {
System.gc();
System.out.println(
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
}
private void loadDocument() throws Exception {
String stringURL = "file:circle.svg";
Reader reader = null;
SVGDocument svgDoc = null;
URL url = new URL(stringURL);
URLConnection connection;
connection = url.openConnection();
reader = new InputStreamReader(connection.getInputStream());
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
doc = f.createSVGDocument("file:circle.svg", reader);
canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);
canvas.setDoubleBufferedRendering(true);
canvas.setSize(new Dimension(800, 600));
canvas.setDocument(doc);
}
public static void main(String[] args) {
JFrame frame = new MemoryLeakTest();
frame.pack();
frame.setVisible(true);
}
}
-------------------------------
circle.svg:
---------------------------
<?xml version="1.0" encoding="windows-1250" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="12cm" height="4cm" viewBox="0 0 1200 400"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<desc>Example circle01 - circle filled with red and stroked with blue</desc>
<circle cx="600" cy="200" r="100"
fill="red" stroke="blue" stroke-width="10" />
</svg>
------------------------------
After pressing stop and then gc one should see "CANVAS removed ***@e2892b" message, but it appears hardly ever.
Regards,
Lukasz