[Kde-bindings] A sample of our subset of QtC

Richard Dale Richard_Dale at tipitina.demon.co.uk
Sat Jan 11 13:17:16 UTC 2003


On Saturday 11 January 2003 12:03 pm, Joseph Wenninger wrote:
> For .Net/Mono/pnet you have to track both, the managed and the reall c++
> object to be able to handle repartending of widgets (eg
> QWidget::reparent) and derived classes correctly, without risking that
> one of them gets deleted by the garbage collector, that's what Adam
> tries to do with the managedObject void* pointer. It makes things easier
I'm having trouble following the C# talk about garbage collection issues 
because the CLI has its own terms for things. 

Anyway, heres how the QtJava memory management works, and why I don't have a 
problem with re-parenting widgets:

Each java instance, wrapping a Qt C++ instance has two instance variables; 
'long _qt' and 'boolean _allocatedInJavaWorld'. '_qt' holds the a pointer to 
the C++ instance (0 if there isn't one yet, or if it's been deleted).

For example:

public class  QFont implements QtSupport {
	private long _qt;
	private boolean _allocatedInJavaWorld = true;
...


 '_allocatedInJavaWorld' is true if the java instance was created via a java 
new statement. But if a C++ instance has been returned from a Qt method which 
doesn't already have a corresponding java instance, the java wrapper instance 
is created and '_allocatedInJavaWorld' is false. Then the java runtime won't 
attempt to delete the C++ instance at garbage collection time - that's up to 
the C++ code which created it.

This hash table is used to keep a mapping between a given C++ instance, and 
the corresponding java wrapper (the key is the address of the C++ instance as 
a hex string):

	/** Uses a C++ key to retrieve the corresponding Java instance */
	public static WeakValueMap	qtKeyToJavaMap = null;

When a java instance is garbage collected, it automatically drops out of this 
map (the value is a weak reference which doesn't prevent garbage collection 
happening).

Each java class has these three methods - dispose(), isDisposed() and 
finalize(). Normally, finalize() is called by the java garbage collection 
runtime and the C++ instance is deleted. 

The dispose() method can be called to delete the C++ instance ahead of garbage 
collection, and then isDisposed() will be true. This is useful when you have 
allocated a large number of large objects like QPixmaps and you've finished 
with them - on windows you're only allowed a finite number of pixmap 
resources.

In the finalize() methods for classes which are descendents and QWidget code 
like this is generated:

JNIEXPORT void JNICALL
Java_org_kde_qt_QScrollBar_finalize(JNIEnv *env, jobject obj)
{
	if (  QtSupport::allocatedInJavaWorld(env, obj) 
             && ((QScrollBar*)QtSupport::getQt(env, obj))->parentWidget(TRUE) 
== (QWidget *) 0) {
		delete (QScrollBar*)QtSupport::getQt(env, obj);
		QtSupport::setQt(env, obj, 0);
	}
	return;
}

A similar test is generated in the finalize() methods that are only 
descendents of QObject, and not QWidgets.

The underlying C++ instance is deleted iff it was allocated via a java new 
statement, and it doesn't have any parent widgets. So a java instance can be 
garbage collected, and still not delete its C++ instance.

Some Qt classes which are not descendents of QObject still need custum 
finalize() code to look for 'parents'. Or the test needs special 
customization, for example, QListViewItem needs this test:

JNIEXPORT void JNICALL
Java_org_kde_qt_QListViewItem_finalize(JNIEnv *env, jobject obj)
{
	if (	QtSupport::allocatedInJavaWorld(env, obj)
			&& ((QListViewItem*)QtSupport::getQt(env, obj))->parent() == 0
			&& ((QListViewItem*)QtSupport::getQt(env, obj))->listView() == 0 )
	{
		delete (QListViewItem*)QtSupport::getQt(env, obj);
		QtSupport::setQt(env, obj, 0);
	}
	return;
}

When a C++ Qt parent instance is deleted, it deletes all it's children which 
may include instances with java wrappers. I added callbacks in the delete() 
mehthods of *JBridge classes like this:

	~QListViewItemJBridge() {QtSupport::qtKeyDeleted(this);

The qtKeyDeleted() method sets '_allocatedInJavaWorld' to false, so these 
child instances aren't deleted twice when their finalize() methods are 
called.

So that's my 'theory of Qt object tree/garbage collection coordination' as 
used in the java bindings.

-- Richard



More information about the Kde-bindings mailing list