Skip to main content

Java2D Disposer mechanism

12 replies [Last post]
cowwoc
Offline
Joined: 2003-08-24

Hi,

The following two BugParade issues discuss how Sun replaced class finalizers using the Java2D Disposer mechanism:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6247526
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6299405

This peaked my curiosity, how does this mechanism work?

Thank you,
Gili

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Jim Graham

There are 2 advantages to Disposer:

- priority of disposal process
- the DisposerTarget mechanism

One problem with the finalize method is that they intentionally leave
some of the aspects of the process undocumented. One thing that is not
documented is the priority of the finalizer thread - which is actually
fairly low. Thus, finalization is not very prompt in the grand scheme
of things and the Java code cannot control that. We asked for
finalization to be increased in priority, but due to compatibility it
was decided to leave it with a low priority.

With Disposer, we can control the priority of the thread managing the queue.

The other problem with finalize() that others have mentioned is that the
object with the finalize() method needs to remain alive. Often, that is
a complicated object that has ancillary data that you don't want to keep
alive for another round of GC. Disposer provides the DisposerTarget
mechanism to deal with a similar problem and so you can use Disposer
without keeping the original object around during the disposal process.

Of course, that latter issue can be managed with finalize() methods as
well. It is common practice to put the finalize() method on the class
that manages the native resources, but one could just as easily have
created an instance of a tiny subclass that is stored as a reference
from the main object and put the finalize() method on the subclass so
the parent can go away and leave that tiny child around in the finalizer
queue. The main thing is that programmers don't often want to design
this way, and so they don't - but DisposerTarget makes this paradigm
more straightforward so it is easier and more likely that the right
thing will be done...

...jim

java2d@JAVADESKTOP.ORG wrote:
> Why would it be cheaper to use WeakReferences to track disposable objects than it would be to use finalizers? Don't both essentially delay garbage-collection and don't both only get flagged as disposable at the GC's discretion? At least with finalizers you're guaranteed that objects will get GC before OutOfMemoryError is thrown, with the WeakReference approach don't you run the risk that you'll run out of native resources and throw OutOfMemoryError when you don't really have to?
>
> Thanks,
> Gili
>
>> But shortly, it uses ReferenceQueue and
>> WeakReferences.
>> It puts a weak reference to the tracked object
>> into a reference queue and when the object is gone
>> the reference is retrieved from the queue,
>> it disposes the resources associated with the
>> object (those resources are kept in a special
>> DisposerRecord object).
> [Message sent by forum member 'cowwoc' (cowwoc)]
>
> http://forums.java.net/jive/thread.jspa?messageID=239733
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

cowwoc
Offline
Joined: 2003-08-24

Hi Dmitri,

I'm curious why the Java2D mechanism uses WeakReference instead of PhantomReference given the problem mentioned here: http://discuss.develop.com/archives/wa.exe?A2=ind0101&L=advanced-java&P=...

Thanks,
Gili

linuxhippy
Offline
Joined: 2004-01-07

> I'm curious why the Java2D mechanism uses
> WeakReference instead of PhantomReference given the
> problem mentioned here:

I also asked this question to myself severla times, because PhantomReferences seem to be exactly designed for what the Disposer uses Weak-References.

I think it could be related to performance, because Phantom-References can't be completly freed in one GC cycle as far as I know, but of course I may be completly wrong ;)

Would be really interesting to know the exact reason from someone who knows for sure ;)

, lg Clemens

Dmitri Trembovetski

Yes, performance was the main consideration. We want
objects to be disposed of as quickly as possible.

Also, if the main issue is 'resurrecting' the object
after taking it off the queue it's not a problem
for the Disposer: we don't do that as all data
which needs disposal is kept in a separate object
(DisposerRecord).

And, if the Disposer is used correctly we don't
even use the reference to the main object itself,
but to its 'disposer referent' - see earlier Jim's
emails.

Thanks,
Dmitri

java2d@JAVADESKTOP.ORG wrote:
>> I'm curious why the Java2D mechanism uses
>> WeakReference instead of PhantomReference given the
>> problem mentioned here:
>
> I also asked this question to myself severla times, because PhantomReferences seem to be exactly designed for what the Disposer uses Weak-References.
>
> I think it could be related to performance, because Phantom-References can't be completly freed in one GC cycle as far as I know, but of course I may be completly wrong ;)
>
> Would be really interesting to know the exact reason from someone who knows for sure ;)
>
> , lg Clemens
> [Message sent by forum member 'linuxhippy' (linuxhippy)]
>
> http://forums.java.net/jive/thread.jspa?messageID=243389
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

cowwoc
Offline
Joined: 2003-08-24

Why would it be cheaper to use WeakReferences to track disposable objects than it would be to use finalizers? Don't both essentially delay garbage-collection and don't both only get flagged as disposable at the GC's discretion? At least with finalizers you're guaranteed that objects will get GC before OutOfMemoryError is thrown, with the WeakReference approach don't you run the risk that you'll run out of native resources and throw OutOfMemoryError when you don't really have to?

Thanks,
Gili

> But shortly, it uses ReferenceQueue and
> WeakReferences.
> It puts a weak reference to the tracked object
> into a reference queue and when the object is gone
> the reference is retrieved from the queue,
> it disposes the resources associated with the
> object (those resources are kept in a special
> DisposerRecord object).

pepijnve
Offline
Joined: 2003-06-10

With finalizers you still have a reference to the original object after it has been marked as garbage. During the finalize invocation it is possible to make the object live again by passing it to some other non garbage object. Due to this possibility the garbage collector has to do another gc pass to see if the object in question can actually be disposed. If I understand it correctly this is the main cause of the performance impact.

When you use References and a ReferenceQueue you no longer have a reference to the disposed object. References that are removed from a ReferenceQueue will always return null when you call get. In other words there is no link to the original object that was refered to. This allows the garbage collector to free the object's memory as soon as it is marked as garbage instead of having to wait till after the finalizer has run.

Brian Goetz explains it much better than I can at http://www.ibm.com/developerworks/library/j-jtp01274.html

cowwoc
Offline
Joined: 2003-08-24

My understanding is that WeakReferences that are registered with a ReferenceQueue will not actually get garbage-collected until someone removes them from the queue. As such, doesn't the GC still have to make a second pass once the reference is removed from the queue?

mthornton
Offline
Joined: 2003-06-10

The referenced object is collected on the first pass, but the WeakReference remains. Usually native resources will be associated with the WeakReference and it is these which aren't disposed until the WeakReference is retrieved from the queue. With a finaliser nothing gets removed until after finalise is called, while with a WeakReference at least the referenced object goes straight away.

cowwoc
Offline
Joined: 2003-08-24

> Usually native resources will be associated with the WeakReference



If I understand you correctly you are saying the following. Given:


1) Font object

2) NativeFont object

3) WeakReference<Font>

4) Map<WeakReference, NativeFont>

5) ReferenceQueue of WeakReferences whose underlying objects have been GCed



Usage scenario:



a) User creates a new Font object

b) a WeakReference is created and the entry is added to the Map

c) the Font object gets garbage-collected some time in the future and its WeakReference is added to the ReferenceQueue.

d) The Disposer thread picks up the WeakReference from the ReferenceQueue, and finds the associated NativeFont using the Map.

e) The Disposer thread deallocates NativeFont and removes WeakReference from the ReferenceQueue



Is that correct?

Dmitri Trembovetski

Hi Gili,

you can take a look at the source at
http://openjdk.java.net, see
j2se/src/share/classes/sun/java2d/Disposer*.java
j2se/src/share/native/sun/java2d/Disposer.c/h

It's relatively well documented..

But shortly, it uses ReferenceQueue and WeakReferences.
It puts a weak reference to the tracked object
into a reference queue and when the object is gone
the reference is retrieved from the queue,
it disposes the resources associated with the
object (those resources are kept in a special
DisposerRecord object).

Thanks,
Dmitri

java2d@JAVADESKTOP.ORG wrote:
> Hi,
>
> The following two BugParade issues discuss how Sun replaced class finalizers using the Java2D Disposer mechanism:
>
> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6247526
> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6299405
>
> This peaked my curiosity, how does this mechanism work?
>
> Thank you,
> Gili
> [Message sent by forum member 'cowwoc' (cowwoc)]
>
> http://forums.java.net/jive/thread.jspa?messageID=239660
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Jim Graham

One thing to add...

I was tracking down an issue that someone was having internally with
using the Disposer and thought that maybe the problem was that they
didn't understand how we use the DisposerTarget interface. Strange that
their query and your email were separated only by minutes. ;-)

It turns out that their problem was unrelated, but in doing my
investigation I noticed that our nicely written doc comments for the
Disposer* classes don't really describe how the DisposerTarget is used,
so I'll try to describe that here:

Basically, a naive user of Disposer might have a Java object which
points at some native data and they will want to register that object
with the Disposer along with some code to dispose the native data.

A problem typically arises in that such objects typically also hold
references to a lot of other data. Since that object was registered in
a queue to track its lifetime, that other data can be prevented from
being garbage collected while the Disposer queue is later processed.
The time might be small, but if the subordinate data is large and you
need it collected right away to satisfy other allocations, you can run
into OutOfMemory situations a lot more easily.

The workaround is to register a different object in the queues - one
that has the same lifetime as the original object which holds the native
resources, but which holds on to little (or nothing) else so its
footprint is small. The DisposerTarget interface allows the Disposer to
ask the object being registered if it has a "proxy" object to stand in
for it in the queues. We use that a lot since most of our classes with
native resources hold on to a lot of data and we don't want them hanging
around in a queue during the Disposer cycle.

You can look at the src/share/classes/sun/java2d/SurfaceData.java class
for an example of how DisposerTarget is used...

...jim

Dmitri Trembovetski wrote:
> Hi Gili,
>
> you can take a look at the source at
> http://openjdk.java.net, see
> j2se/src/share/classes/sun/java2d/Disposer*.java
> j2se/src/share/native/sun/java2d/Disposer.c/h
>
> It's relatively well documented..
>
> But shortly, it uses ReferenceQueue and WeakReferences.
> It puts a weak reference to the tracked object
> into a reference queue and when the object is gone
> the reference is retrieved from the queue,
> it disposes the resources associated with the
> object (those resources are kept in a special
> DisposerRecord object).
>
> Thanks,
> Dmitri
>
> java2d@JAVADESKTOP.ORG wrote:
>> Hi,
>>
>> The following two BugParade issues discuss how Sun replaced class
>> finalizers using the Java2D Disposer mechanism:
>>
>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6247526
>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6299405
>>
>> This peaked my curiosity, how does this mechanism work?
>>
>> Thank you,
>> Gili
>> [Message sent by forum member 'cowwoc' (cowwoc)]
>>
>> http://forums.java.net/jive/thread.jspa?messageID=239660
>>
>> ===========================================================================
>>
>> To unsubscribe, send email to listserv@java.sun.com and include in the
>> body
>> of the message "signoff JAVA2D-INTEREST". For general help, send
>> email to
>> listserv@java.sun.com and include in the body of the message "help".
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

cowwoc
Offline
Joined: 2003-08-24

That's a nice explanation though I suggest you add it into the source-code because the likelihood of someone finding the explanation here is much lower than in the source :)

Gili

> It turns out that their problem was unrelated, but in
> doing my
> investigation I noticed that our nicely written doc
> comments for the
> Disposer* classes don't really describe how the
> DisposerTarget is used,
> so I'll try to describe that here: