Hello all,
I found an always-reproducable memory leak with GLJPanel but I do not know where I can report the bug… Hopefully, somebody here will tell me where the JOGL’s bug database resides!
The memory leak happens everytime a GLJPanel is detached from the components arborescence. For instance, if a GLJPanel is added in a JFrame, when calling removeAll() on that JFrame, the GLJPanel is still indirectly held by the following reference chain:
-> java.awt.EventDispatchThread@0x9501d788 (117 bytes) (field threadLocals:)
--> java.lang.ThreadLocal$ThreadLocalMap@0x9501f718 (20 bytes) (field table:)
--> [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;@0x9501f730 (72 bytes) (Element 5 of [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;@0x9501f730:)
--> java.lang.ThreadLocal$ThreadLocalMap$Entry@0x9501f7e0 (28 bytes) (field value:)
--> com.sun.opengl.impl.GLPbufferImpl$InitAction@0x9503f880 (12 bytes) (field this$0:)
--> com.sun.opengl.impl.GLPbufferImpl@0x9503f890 (48 bytes) (field drawableHelper:)
--> com.sun.opengl.impl.GLDrawableHelper@0x950476c0 (13 bytes) (field listeners:)
--> java.util.ArrayList@0x950476d0 (20 bytes) (field elementData:)
--> [Ljava.lang.Object;@0x950476e8 (12 bytes) (Element 0 of [Ljava.lang.Object;@0x950476e8:)
--> javax.media.opengl.GLJPanel$Updater@0x950476f8 (16 bytes) (field this$0:)
--> GLJPanel@0x95047888 (514 bytes)
It appears that the GLPbufferImpl$InitAction is never released. Since it is a non-static nested class, it maintains the GLPbufferImpl in memory and then, the GLJPanel through a chain of event listeners.
I reproduced the bug with a simple application: it shows a JFrame that contains either a GLJPanel or a JPanel or both. Two menu entries allow to separately add or remove the GLJPanel and the JPanel from the JFrame’s arborescence. By using jmap and jhat utilities (or any other memory profiler), one can verify that when the JPanel is removed, it properly becomes eligible for garbage collection but when the GLJPanel is removed, it is still held by the same reference chain than described above. In that application, the JPanel is shown in green and the GLJPanel will certainly appear black since it does not display any OpenGL scene.
Here is the code of my small application:
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.media.opengl.GLJPanel;
import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
public class Program
{
public static void main(final String[] args)
{
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
static Frame frame = new Frame();
}
class Frame extends JFrame
{
private static final long serialVersionUID = 1L;
private boolean isPanelVisible = true;
private boolean isGLPanelVisible = true;
public Frame()
{
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
menu.add(new ShowPanelAction());
menu.add(new ShowGLPanelAction());
this.setJMenuBar(menuBar);
this.setSize(400, 200);
this.setLayout(new BoxLayout(this.getContentPane(), BoxLayout.X_AXIS));
this.update();
}
void changePanelVisibility()
{
this.isPanelVisible = !this.isPanelVisible;
this.update();
}
void changeGLPanelVisibility()
{
this.isGLPanelVisible = !this.isGLPanelVisible;
this.update();
}
private void update()
{
this.getContentPane().removeAll();
if (this.isPanelVisible)
{
this.add(new Panel());
}
if (this.isGLPanelVisible)
{
this.add(new GLPanel());
}
this.validate();
this.repaint();
}
}
class ShowPanelAction extends AbstractAction
{
private static final long serialVersionUID = 1L;
public ShowPanelAction()
{
super("Show/Hide JPanel");
}
@Override
public void actionPerformed(ActionEvent e)
{
Program.frame.changePanelVisibility();
}
}
class ShowGLPanelAction extends AbstractAction
{
private static final long serialVersionUID = 1L;
public ShowGLPanelAction()
{
super("Show/Hide GLJPanel");
}
@Override
public void actionPerformed(ActionEvent e)
{
Program.frame.changeGLPanelVisibility();
}
}
class Panel extends JPanel
{
private static final long serialVersionUID = 1L;
public Panel()
{
this.setBackground(Color.green);
}
}
class GLPanel extends GLJPanel
{
private static final long serialVersionUID = 1L;
}
I run a Debian linux box with Jogl 1.1.1.
Any help that tell me how I could report this bug in the right place would be greatly appreciated.
Many thanks!
Cyrille