[Qt-jambi-interest] Memory Leaks - any updates?
Gunnar Sletta
gunnar at trolltech.com
Thu Nov 27 16:36:12 CET 2008
Curt Nowak wrote:
> Hi all,
>
> has anyone else experienced memory leaks similar to what I had
> described a couple of weeks ago? Is this my fault or Jambi's? Is
> there a known workaround? Gunnar mentioned a "reference to the
> top-level" that is retained by mistake. It would seem that every
> apllication that creates new QWidgets during runtime would encounter
> this issue. Once again I attached my short example program.
Hi Curt,
We've finally had the time to dig into this issue and the problem lies
somewhat in the usecase, though ever so hidden ;)
The explanation requires some dirty details, so feel free to ask again
if its not clear.
By default any jambi object that is allocated is created with what we
call Java ownership, this means it is GC'ed by default as any other Java
object. This works fine as along as all the objects are only referenced
by the virtual machine. When you build a widget hierarchy however, you
tend to create components, put them in a layout and forget about them,
for instance:
QWidget window = new QWidget();
QHBoxLayout box = new QHBoxLayout();
QLabel label = new QLabel("Name:");
QLineEdit edit = new QLineEdit();
box.addWidget(label);
box.addWidget(edit);
window.setLayout(box);
return window;
What happens above is that you create a window, a layout and two
controls. The controls are added to the layout and the layout is then
set on the control. In setting the layout, the controls are reparented
to become children of the window. Then we return the window and forget
about everything but the toplevel window. If we had followed strict Java
ownership rules, the references to "box", "label" and "edit" would no
longer exist and GC would kill them.
This is naturally not what you want so we introduced the concept of C++
ownership which happens behind the scenes. There are a lot of various
uses for this, but the primary one, and the one relevant for the pooling
of memory is that when a widget has a parent (actually when any
com.trolltech.qt.core.QObject subclass has a parent), the parent has
ownership over that object. So when a widget or layout is part of a
hierarchy like above we keep a JNI Global reference to the widget, local
reference would not be good enough as the childwidgets would then be
collected. When a parent goes away, it takes the children with it.
So in the case of:
QWidget window = new QWidget();
QPushButton button = new QPushButton(window);
The button is retained through JNI and window is retained through normal
Java reference handling. When window is no longer referenced, it will be
collected, taking button and the JNI retained reference with it.
Now.. back to the real problem... The code was
QWidget window = new QWidget();
QPushButton button = new QPushButton(window) {
public void disposed() {
// something...
}
}
Qt Jambi relies on GC collecting the window and thus taking the button
with it, but because the button is an anonymous inner class of the
QPushButton, the QPushButton$1 class will have an implicit reference
back to the window object. Because the QPushButton$1 reference has a
global reference in JNI it will never be deleted by the garbage
collector and thus, there will always at least one reference to the
window, the one in the inner class. Thus, one has created a circular
reference that the GC cannot detect and none of them will be collected.
By adding debugging data to your application to track a problem, you
incidentally created the problem.
Because of the rather common use of anonymous inner classes in Java, I
think I will have to rephrase my previous statements about memory
management and claim that all top level widgets should be explicitly
disposed. That will solve the problem because that will delete both the
C++ widget and the C++ button, releasing the JNI ref and thus leaving
the Java parts of the objects free for grabs by the GC.
I'll also add this to the documentation.
best regards,
Gunnar
More information about the Qt-jambi-interest
mailing list