Java Forum / GUI / November 2004
Thread-question
Ao - 05 Nov 2004 16:02 GMT Hi everybody,
when closing my program I would like to start a Thread which does some cleaning up in the background, but opf course before exiting I need to be sure, that this Thread has finished running...
How can I wait for that Thread to finish, the following code does work, but freezes my UI...
... private boolean cleanedUp = false;
... public void programmBeenden() { Thread cleaner = new Thread() { public void run() { // clean up some things... cleanedUp=true; } } cleaner.start();
// ... do some other stuff // finally wait for cleaner to finish while(!cleanedUp) { try { Thread.sleep(100); } catch(InterruptedExecption iuE) {} }
System.exit(0); }
What else can I do to make sure that cleaner has finished without freezing my UI.
Thanks, Aloys
Thomas Weidenfeller - 05 Nov 2004 16:31 GMT Please post to one group only. Your question is not that important.
> How can I wait for that Thread to finish, the following code does work, > but freezes my UI... There is some information about this in the comp.lang.java.gui FAQ. You are blocking the EDT.
Maybe you also want to read about shutdown hooks, SwingWorker and the Swing threading model in general.
Regarding the problem of waiting for a thread: Thread.join();
/Thomas
shakahshakah@gmail.com - 05 Nov 2004 16:32 GMT FWIW, and depending on the what you are trying to accomplish, you may not have to explicitly wait for the worker thread to finish. In the absence of an explicit call to System.exit(), your application won't exit until all (non-daemon) threads have exited.
xarax - 05 Nov 2004 20:55 GMT > FWIW, and depending on the what you are trying to accomplish, you may > not have to explicitly wait for the worker thread to finish. In the > absence of an explicit call to System.exit(), your application won't > exit until all (non-daemon) threads have exited. The AWT EDT is non-daemon, thus the call to System.exit() is required to shutdown the JVM.
Just put the System.exit(0) call in the other thread that performs the clean-up. Don't bother with Thread.join() in the EDT; it will just hang the GUI.
hiwa - 06 Nov 2004 01:15 GMT > but freezes my UI... See Q3.1, 3.2 and 4 .2 of the FAQ: http://groups.google.com/groups?hl=en&lr=&selm=cjr8er%24oo0%241%40newstree.wise. edt.ericsson.se
Also, Runtime#addShutdownHook() may be of your service.
Babu Kalakrishnan - 06 Nov 2004 08:00 GMT > when closing my program I would like to start a Thread which does some > cleaning up in the background, but opf course before exiting I need to > be sure, that this Thread has finished running... Runtime.getRuntime().addShutdownHook()
BK
Yakov - 06 Nov 2004 13:33 GMT > Hi everybody, > > when closing my program I would like to start a Thread which does some > cleaning up in the background, but opf course before exiting I need to > be sure, that this Thread has finished running... If you want to exit your app only after some code is finished, do not even bother with threads. Just create another method with required code, and the last line of your program should be a call of this method.
Thomas G. Marshall - 09 Nov 2004 15:17 GMT Yakov coughed up:
>> Hi everybody, >> [quoted text clipped - 6 lines] > code, and the last line of your program should be a call of this > method. That is not going to work in general.
His "code" that you refer to could easily just be firing up a pile of GUI and then do nothing more. Setting up the GUI is (usually) by nature non-blocking, this leaves no particular good time for that last line of his program to run.
The other suggestions in this thread are better.
 Signature http://www.allexperts.com is a nifty way to get an answer to just about /anything/.
Andy Flowers - 09 Nov 2004 18:44 GMT Have you thought about overriding finalize() in the objects you wish to have some final clean up ?
> Hi everybody, > [quoted text clipped - 33 lines] > > Thanks, Aloys Carl Howells - 09 Nov 2004 18:48 GMT > Have you thought about overriding finalize() in the objects you wish to have > some final clean up ? Bad idea. On most modern JVMs, finalizers seriously mess up garbage collection. In fact, if the JVM can avoid it, it'll just *not* collect objects with finalizers. It's not a very good way to get code run.
If you need post-collection cleanup of non-memory resources, remember that PhantomReference is your friend.
Chris Uppal - 10 Nov 2004 09:16 GMT > Bad idea. On most modern JVMs, finalizers seriously mess up garbage > collection. I have to say that I find this implausible. Obviously finalisation adds overhead to the GC operation, but that's because it's doing extra work -- /useful/ extra work -- but it sounds as if you mean something more than that. Do you have any references ?
> In fact, if the JVM can avoid it, it'll just *not* collect > objects with finalizers. It's not a very good way to get code run. It's perfectly at liberty not to collect anything. But again you seem to be going beyond that and stating that it (they) will discriminate against objects with finalisers. Again, this sounds implausible. Do you have a reference for it ?
(BTW I don't think finalisation is likely to be at all appropriate as a solution to the OP's problem.)
-- chris
John C. Bollinger - 10 Nov 2004 14:40 GMT >>Bad idea. On most modern JVMs, finalizers seriously mess up garbage >>collection. [quoted text clipped - 3 lines] > /useful/ extra work -- but it sounds as if you mean something more than that. > Do you have any references ? I haven't looked up references (yet) but my understanding is that Carl is correct, if perhaps overly emphatic. Based only on the API docs and JLS, one can argue that instances of classes that do not override finalize() can be collected without actually invoking Object.finalize(), as the VM can know that its Object.finalize() is specified to do nothing. More importantly, however, it is possible for a finalize() method to make the object it is invoked on reachable again (an odd state indeed: finalized and reachable), so after finalizing an object with a non-default finalizer, the GC must completely redo its reachability analysis for that object. Naturally, that also affects the reachability of objects to which the one in question holds references.
The VM must also keep track of whether or not reachable objects have been finalized, because they are only ever finalized once (per specification). That probably doesn't add much overhead, but it may result in unexpected behavior when the second time an object becomes unreachable it is discarded without its finalizer being invoked.
>>In fact, if the JVM can avoid it, it'll just *not* collect >>objects with finalizers. It's not a very good way to get code run. [quoted text clipped - 3 lines] > with finalisers. Again, this sounds implausible. Do you have a reference for > it ? That would be an implementation question, but I suspect that many Java GC implementations indeed do exactly as Carl describes. It really is a much bigger deal to collect an object with a non-default finalizer than to collect other objects.
It might be a little too strong to say that Java's finalization mechanism is broken, but I have never personally run into any task to which it is suited. It is definitely a trap, because people -- especially those with experience in some other OO languages, such as C++ -- tend to make incorrect assumptions about how finalization works. It has been said here before and it will be repeated again: a Java finalizer is not at all the same thing as a C++ destructor.
John Bollinger jobollin@indiana.edu
Thomas G. Marshall - 10 Nov 2004 15:32 GMT John C. Bollinger coughed up:
>>> Bad idea. On most modern JVMs, finalizers seriously mess up garbage >>> collection. [quoted text clipped - 22 lines] > result in unexpected behavior when the second time an object becomes > unreachable it is discarded without its finalizer being invoked. There are a few places within the java documentation, api and JLS, where /required/ behavior is not /quite/ pinned down to my liking. Precisely /what/ the GC is required to do on various platforms is one of them. I consider it one of my bigger gripes.
...[rip]...
 Signature "Gentlemen, you can't fight in here! This is the War Room!"
John C. Bollinger - 10 Nov 2004 16:08 GMT > John C. Bollinger coughed up: >>I haven't looked up references (yet) but my understanding is that Carl [quoted text clipped - 20 lines] > /what/ the GC is required to do on various platforms is one of them. I > consider it one of my bigger gripes. There is a tension in most language and API specifications between pinning down requirements very precisely so as to provide the least ambiguous definitions, and describing requirements more generally so as to allow more flexibility in implementation. Too far in either direction doesn't make sense. Moreover, implementations typically operate under an "as if" principle (any behavior is acceptable as long as to any external observer it is indistinguishable from the specified behavior).
In the case of GC, the API docs for Object.finalize() give a fairly extensive discussion of the question, and JLS(2e) 12.6 covers the topic in (IMO) reasonably complete detail, including an object lifecycle diagram in 12.6.1. I believe everything I discussed (above) is in fact described by the specification, even including the VM skipping invocation of finalize() when it has not been overridden. I am satisfied with the precision of the specification in that area; what details would you prefer to see more completely specified?
John Bollinger jobollin@indiana.edu
Thomas G. Marshall - 10 Nov 2004 19:28 GMT John C. Bollinger coughed up:
>> John C. Bollinger coughed up: >>> I haven't looked up references (yet) but my understanding is that [quoted text clipped - 38 lines] > I am satisfied with the precision of the specification in that area; > what details would you prefer to see more completely specified? Not from finalize in particular, but from the gc in general.
Take for example System.gc() api doc:
Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.
That last statement is misleading. It does not have to make a "best effort" at that point at all. It's "best effort" can easily be to ignore the call entirely without attempt.
 Signature Whyowhydidn'tsunmakejavarequireanuppercaselettertostartclassnames....
John C. Bollinger - 12 Nov 2004 17:41 GMT > John C. Bollinger coughed up: >>I am satisfied with the precision of the specification in that area; [quoted text clipped - 15 lines] > at that point at all. It's "best effort" can easily be to ignore the call > entirely without attempt. Well, what would you prefer that it say? Would it satisfy you if something were added to that documentation to the effect of "The definition of 'best effort' depends on the memory management implementation in use in the Virtual Machine at the time of this method's invocation. It is possible that the implementation's 'best effort' is in fact no effort at all."? That wouldn't be any more specific, but it would at least be explicit about its lack of specificity.
Would you be willing to restrict the possible GC implementations by specifying their required operation in more detail? More importantly, how does the lack of detail in these areas interfere with your ability to develop Java programs?
John Bollinger jobollin@indiana.edu
Chris Uppal - 12 Nov 2004 09:50 GMT > > > Bad idea. On most modern JVMs, finalizers seriously mess up garbage > > > collection. [quoted text clipped - 7 lines] > is correct, if perhaps overly emphatic. Based only on the API docs and > JLS, [...discussion snipped...] I agree that it will make the GC system do more work, but I don't see that it's "messing up" the GC, just that finalisation interacts with GC.
The "obvious" way to implement it (actually, I can't think of a better way, but I suspect there almost certainly /are/ better ways) would go something like this:
When an object with a non-empty finalize() method is created, a flag is set in its header -- meaning "must finalise". When GC discovers an object to be "unreachable" it checks the flag, if that flag is not set then it <does whatever it would normally>, otherwise it adds it to a queue serviced by a finaliser thread (so the object has now become reachable again), and moves on. The finaliser thread sits waiting on the queue; when something appears, it takes it off the queue, clears the "must finalise" flag, and executes the object's finalize() method (trapping and ignoring all exceptions). It then moves on to the rest of the queue. If the object has not become reachable again then when the GC next re-visits it, it will be reclaimed.
Now if a real implementation is anything like the above, then I don't think its helpful to describe finalisation as "messing up" the GC. The system as a whole has to do a little more work for each finalisable object -- but that's fair, we /want/ it to do more work otherwise the finaliser wouldn't get run. But the GC itself is just doing its normal job and is not impeded by the presence of finalisable objects. (Of course it could run faster if the spec didn't allow finalisation at all, in which case there would be no need to check any flags).
Notice how the finalisable object's finalise method can get called almost arbitrarily later than when it was first discovered to be unreachable. Notice also that its eventual reaping is also postponed, happening at least one GC cycle later than when it was first "noticed" by the GC, even if the finaliser thread is keeping up with the rate that objects are put on its queue. But those delays are not symptomatic of the system being prevented from working properly, they are just the expected result of the design.
> > It's perfectly at liberty not to collect anything. But again you seem > > to be going beyond that and stating that it (they) will discriminate [quoted text clipped - 5 lines] > much bigger deal to collect an object with a non-default finalizer than > to collect other objects. I don't think that's the right way to look at it. It is no bigger deal to GC a finalisable object than any other (except, of course, that it has to be GCed twice). The lifetime of a finalisable object is more complicated than other objects -- naturally -- and the system does more work as the object dies than it would otherwise need to, but that's just natural. It's sort of like saying "don't make objects serialisable since the system will have to do more work when it serialises them" ;-) That's not to say that finalisation's /free/, or even that it's /cheap/ -- it has costs, just like anything else.
For comparison: it /would/ be fair to describe it as "messing up" GC if the actual use of finalisation made other objects harder to re-claim. For instance if the implementation of reference queues were such that every reclaimed object had to be checked against all the existing queues (impossible to believe the implementation /is/ like that!), then each new queue would add extra load to the /whole/ system. In such a case it would be very correct to say that reference queues mess up GC.
> It might be a little too strong to say that Java's finalization > mechanism is broken, Unless, of course, it actually /is/ broken -- that's an implementation question. I haven't heard of any problems with the Sun implementation myself, but there could easily (-ish) /be/ such problems that Carl has heard of but I had not. Which is why I asked.
> but I have never personally run into any task to > which it is suited. It is definitely a trap, because people -- > especially those with experience in some other OO languages, such as C++ > -- tend to make incorrect assumptions about how finalization works. It > has been said here before and it will be repeated again: a Java > finalizer is not at all the same thing as a C++ destructor. It can certainly mislead C++ programmers -- we see it here often (I'm sure you're as sick of it as me). It can also be abused, especially if people think they can use it to clean up and reclaim system resources that are significantly more scarce than normal object memory. (The incredibly stupid example of NIO system buffers leaps to mind). But I think it has its uses. As I've said before, finalisation is (IMO) best viewed as a kind of reflection -- reflecting on an object's lifetime -- and like all reflection it's not something you are going to need every day. Depending on what kind of programming you do, you can go a lifetime and never use it. But if you do need it, then /boy/ do you need it...
-- chris
Carl Howells - 10 Nov 2004 18:54 GMT >>Bad idea. On most modern JVMs, finalizers seriously mess up garbage >>collection. [quoted text clipped - 11 lines] > with finalisers. Again, this sounds implausible. Do you have a reference for > it ? Observed behavior. I just wrote up this test:
----------- Test2.java ----------- import java.util.*; import java.lang.ref.*;
public class Test2 { public static void main(String [] args) { System.out.println(System.getProperty("java.version")); System.out.println(System.getProperty("java.vendor")); System.out.println();
int [] sizes = {10, 100, 1000, 10000, 100000, 1000000};
for (int i = 0; i < sizes.length; i++) { test(sizes[i]); } }
private static void test(int size) { ReferenceQueue q = new ReferenceQueue(); Set s = new HashSet();
int count = 0; int nfcount = 0; int wfcount = 0;
while (count < size) { s.add(new BooleanReference(new Object(), q, false)); s.add(new BooleanReference(new WithFinalizer(), q, true));
BooleanReference br; while ((br = (BooleanReference)q.poll()) != null) { s.remove(br); count++; if (br.flag) { wfcount++; } else { nfcount++; } } }
System.out.println(count + " object collected."); System.out.println("Objects with finalizer collected: " + wfcount); System.out.println("Objects without finalizer collected: " + nfcount); System.out.println(); }
private static class WithFinalizer { private static int whatever;
protected void finalize() { whatever++; } }
private static class BooleanReference extends PhantomReference { public boolean flag;
public BooleanReference(Object referent, ReferenceQueue q, boolean flag) { super(referent, q); this.flag = flag; } } } ----------- end Test2.java -----------
I had a bit of an issue with an earlier version, as when I wasn't saving BooleanReferences they were getting collected before the objects they were referring to, which kind of made things more difficult. :)
Anyway, here's the output of two different runs through it:
1.4.2_03 Sun Microsystems Inc.
242 object collected. Objects with finalizer collected: 0 Objects without finalizer collected: 242
5723 object collected. Objects with finalizer collected: 0 Objects without finalizer collected: 5723
5673 object collected. Objects with finalizer collected: 0 Objects without finalizer collected: 5673
12115 object collected. Objects with finalizer collected: 1957 Objects without finalizer collected: 10158
113516 object collected. Objects with finalizer collected: 45157 Objects without finalizer collected: 68359
1093806 object collected. Objects with finalizer collected: 487562 Objects without finalizer collected: 606244
-----------
1.5.0 Sun Microsystems Inc.
211 object collected. Objects with finalizer collected: 0 Objects without finalizer collected: 211
1288 object collected. Objects with finalizer collected: 0 Objects without finalizer collected: 1288
5707 object collected. Objects with finalizer collected: 0 Objects without finalizer collected: 5707
20398 object collected. Objects with finalizer collected: 5711 Objects without finalizer collected: 14687
111297 object collected. Objects with finalizer collected: 46748 Objects without finalizer collected: 64549
1026543 object collected. Objects with finalizer collected: 476431 Objects without finalizer collected: 550112
-----------
So, there's definately a bias against objects with finalizers in those JVMs. The bias becomes less extreme as more and more objects are collected, but it's still present.
Chris Uppal - 12 Nov 2004 10:18 GMT > Observed behavior. I just wrote up this test: An interesting example, thanks.
> So, there's definately a bias against objects with finalizers in those > JVMs. The bias becomes less extreme as more and more objects are > collected, but it's still present. I think that you are seeing the effect of objects having to wait on the finaliser thread's queue (see my reply to John Bollinger). Unless your test was running on a multi-processor machine (and possibly even if it was) then the finaliser will have been contending for CPU time with your executing loops, and so it won't have been able to run as often as it would "like to". If that surmise is correct, then adding a small delay to your loop should give it a chance to run and clean up.
Notice, by the way, that your use of reference queues is essentially the same as how the system (I think) implements finalisation. The GC sees that an object is "special" and instead of just wiping it, it places it on a queue for some other thread (or other processing) to deal with. In the case of your example, it is placing the reference object onto the queue rather than the dead object itself, but the logic is similar to finalisation (though I'd guess that it's marginally more expensive to execute). The bit that corresponds to the finaliser thread's activity is your loop where you poll() the queue.
From the look of the results you quoted, your own loop has a slight advantage over the finaliser thread's loop, but I don't know whether that is simply reflecting an "unfair" advantage due to being scheduled more often, or is because of some other aspect of the implementation. At the minimum, there's an "unfair" advantage to your loop in that the results are reported when /it/ has finished, but without also waiting for the finaliser to finish. In a sense, your loop gets to decide when the "race" is over, so even if it and the finaliser were working at the same speed on average, we'd never see the finaliser "beat" your loop, we could only ever see it "draw" or "loose".
-- chris
Thomas G. Marshall - 12 Nov 2004 15:31 GMT Chris Uppal coughed up:
>> Observed behavior. I just wrote up this test: > [quoted text clipped - 18 lines] > places it on a queue for some other thread (or other processing) to > deal with. Yeah, I'm guessing that's right. The end result is that there is a reference /somewhere/ in all the gobledy gook (in a queue with all the other finalizer objects) that keeps the object from being marked for the M&S gc algorithm.
> In the case of your example, it is placing the reference > object onto the queue rather than the dead object itself, but the > logic is similar to finalisation (though I'd guess that it's > marginally more expensive to execute). The bit that corresponds to > the finaliser thread's activity is your loop where you poll() the > queue. I think your conclusion so far has been fairly on the money here. It isn't ruining the gc algorithm per se, it is merely making it even /harder/ to predict.
> From the look of the results you quoted, your own loop has a slight > advantage over the finaliser thread's loop, but I don't know whether [quoted text clipped - 9 lines] > > -- chris
 Signature http://www.allexperts.com is a nifty way to get an answer to just about /anything/.
Gordon Beaton - 12 Nov 2004 10:49 GMT >> Bad idea. On most modern JVMs, finalizers seriously mess up garbage >> collection. [quoted text clipped - 3 lines] > extra work -- /useful/ extra work -- but it sounds as if you mean > something more than that. The extra work done by finalization may be useful, but there is no added value if you could have done it yourself with a suitable dispose() method. However the cost is there regardless.
> Do you have any references ? Here's one: http://servlet.java.sun.com/javaone/javaone2000/event.jsp?eventId=1172
(In particular, pages (approx) 30-36 of the slides)
Although this was presented 4 years ago and some implementation details have likely changed since then, several of the arguments he presents against finalization (e.g. average age of objects and the resulting effect on process size) certainly hold regardless of implementation.
In particular, one aspect he mentions that seems to be missing from this discussion, is the tree of objects that are artificially kept alive because of references from (other) objects waiting for finalization. Longer average object life leads to more live objects at any given time, and consequently more work for the garbage collector.
If I've understood correctly, objects that use finalization must be queued for gc a second time, after finalization has run.
Similar comments can be found in appendix A "The truth about garbage collection, in "Java platform performace": http://java.sun.com/developer/Books/performance/
Here's another: http://www-106.ibm.com/developerworks/java/library/j-jtctips/j-jtc0319a.html
/gordon
 Signature [ do not email me copies of your followups ] g o r d o n + n e w s @ b a l d e r 1 3 . s e
Chris Uppal - 16 Nov 2004 11:49 GMT [re-ordered]
> [...] seems to be missing from > this discussion, is the tree of objects that are artificially kept > alive because of references from (other) objects waiting for > finalization. Longer average object life leads to more live objects at > any given time, and consequently more work for the garbage collector. This is undoubtedly true. Still, I'm finding it hard to imagine convincing examples of the proper use of finalisation where this would be an issue (but see below).
BTW, don't forget that /not/ relying on finalisation can have costs too. More, and more complicated, code. More opportunity for error. Even, potentially larger object networks -- e.g. if an object that may need cleanup cannot just be handed-over to another context and forgotten, then the recieving context may have to keep a link back to the supplier so that it can "hand back" the object when it has finished with it.
One thing that this discussion has brought into focus for me, which I hadn't noticed before, is that not allowing the programmer to manipulate the "must finalise" flag (posited in an early post) is a fairly big design error in Java. For one thing, as everyone knows, finalisation only ever happens once -- which is a very strange situation for an object that is allowed to "resurect" itself in its finaliser. But the more serious problem is that there is no way for us to tell the JVM that finalisation will /not/ be needed for a given object. For instance when we clean up explicitly in a cleanUp() method, then we should be able to say something like: this.beNotFinalizable() to clear the (hypothetical) "must finalise" flag and allow the object to die normally without the various overheads (including keeping object networks alive) of finalization.
Actually, I'd prefer to have beFinalizable() and beNotFinalizable() as (probably protected) methods of Object, and for "finalisability" to be off for all objects, by default, regardless of whether they defined a finalize() method. I.e. it should be the programmer's responsibility to manipulate finalisability as necessary, not left up to an ill-defined system "optimisation".
> > Do you have any references ? > > Here's one: > http://servlet.java.sun.com/javaone/javaone2000/event.jsp?eventId=1172 > > (In particular, pages (approx) 30-36 of the slides) I get the impression from those slides that he's attempting to "demonise" finalisation. It's difficult to tell from presentation slides, but if I saw the same claimes in a carefully worded article, then I'd accuse him of muddled thinking and slanted examples. (<aside> why-oh-why do people /insist/ on putting presentation slides on the Web ? They are never any use -- presentation slides /shouldn't/ have enough words to be any use out-of-context. It seems lazy to me </aside>). I imagine that the backgound was that he was seeing abuse and over-reliance on finalisation, and was attempting to discourage its use.
-- chris
John C. Bollinger - 16 Nov 2004 16:54 GMT > One thing that this discussion has brought into focus for me, which I hadn't > noticed before, is that not allowing the programmer to manipulate the "must [quoted text clipped - 16 lines] > finalisability as necessary, not left up to an ill-defined system > "optimisation". I think I have to disagree with you on this one, Chris. The programmer can already control this dimension to a limited extent by deciding whether or not to override Object.finalize(). To be sure, that's a per-class decision, not a per-object one, but I think that's where the decision belongs. It is also a design-time decision, not a runtime one, but I'm okay with that. I would rather put the onus on the designer to adequately consider this issue than leave the developer to hack it out as he sees fit (even if the same person operates in both roles). I am particularly not keen on the increased uncertainty about whether a finalizer might run, and about how many times it might run.
Do you have a concrete-ish example of how your version of finalization management would be an improvement over what Java already offers? I appreciate the general desire to have more control, but I don't really see what it would gain you in this case.
John Bollinger jobollin@indiana.edu
Chris Uppal - 20 Nov 2004 14:39 GMT > > Actually, I'd prefer to have beFinalizable() and beNotFinalizable() as > > (probably protected) methods of Object, and for "finalisability" to be [quoted text clipped - 8 lines] > per-class decision, not a per-object one, but I think that's where the > decision belongs. It's probably "agree to differ" time then ;-) At a guess you a reasoning at least partly by analogy with ordinary GC, where /not/ leaving memory allocation/deallocation under the control of the programmer has too many advantages to list ?
My position is that finalization itself is a fairly "heavy-weight" operation, and as such is less well-suited to automation. Mainly because there's more to loose when (or if) the system doesn't handle it as well as I/we could if we were doing it by hand. (There's a definite parallel there with closing file handles, which are too heavyweight to be suitable for handling completely automatically -- by finalisation, for instance ;-)
The other reason I favour the more explicit approach is that I work with it often and find it "feels" better (as I've mentioned before, I do more work in Smalltalk than Java, and that's how the implementation I use works). There is an efficiency gain, of course, to being able to turn finalisation on only as and when needed (during any specific object's total lifetime), and I don't think that should be ignored. But the other side of the coin is that I find that code with explicit control of finalisation is actually /clearer/ than the equivalent would be in Java. That's because the normal pattern (virtually universal) is that you do a (using Java syntax) this.beFinalizable(); at the time when you acquire a resource that must be released (such as an open OS file handle -- this is internal to the object that wraps that handle, of course); and you do a this.beNotFinalizable(); at the time when you relinquish that resource. I find that those methods act as flags that show very clearly how the object does its resource management. You might say that they form the skeleton of the design, and once you can see that clearly, then it's easier to see where the flesh fits around it.
I find it particularly helpful in cases where the object is normally cleaned up explicitly, but may also be handled by finalisation as a fall-back for when explicit clean-up would overcomplicate the design. In such cases it can be difficult to get a handle on where the responsibility lies -- which methods you /may/ call, which ones you /must/ call, which ones you must /not/ call more than once... In such cases I start by looking for beFinalizable()/beNotFinalizable() and the whole thing then normally becomes crystal clear.
> Do you have a concrete-ish example of how your version of finalization > management would be an improvement over what Java already offers? Two somewhat indirect examples, and coming from slightly different directions. I don't know how convicing you'll find them, but at least they are both real examples, taken from the Smalltalk system I work with:
Database connections, and other similarly heavy-weight database-related objects (prepared statements, etc) are best cleaned up explicitly (IMO). They also may be complicated enough (internally) for the concerns that Gordon mentioned (keeping object networks alive, etc) to be an issue in practise. However it would also be unthinkable (for various reasons) not to make open DB connections clean up by finalisation, where necessary. Notice also that DB code can (in some cases) place considerable strain on the finaliser thread since the code may not be interactive (so no handy pauses to work in while the user thinks). The end result of that is that you want to be able to turn off "finalisability" in the clean-up code, or else you have to write extra code to ensure that the cleaned-up but still finalisable object puts as little strain on the system as possible.
The other example is about managing Windows GDI handles. In this case the resource to be managed is quite lightweight, and there's no great problem with relying mostly on finalisation to clean-up (which simplifies the system very considerably, or at least allows significantly greater flexibility for a given degree of complexity). What /is/ a problem is understanding how the handles are managed, especially since some are "owned", and must be cleaned up, and some are "shared" and must /not/ be cleaned up, but the two are otherwise interchangeable. In this case I think the biggest benefit of explicit control of finalisation is just that: it /is/ explicit. It makes it easier to understand how that part of the system fits together.
-- chris
John C. Bollinger - 22 Nov 2004 15:32 GMT >>>Actually, I'd prefer to have beFinalizable() and beNotFinalizable() as >>>(probably protected) methods of Object, and for "finalisability" to be [quoted text clipped - 13 lines] > allocation/deallocation under the control of the programmer has too many > advantages to list ? I think I'm coming more from the point of view that Java finalization is inherently unsuited to so many kinds of tasks that I would prefer to not offer additional hooks -- it would just make it that much easier to write broken code.
In fact, I think I am shifting my stance on finalization in general from "I don't know what it's good for" to "it isn't good for anything." Objects may not be finalized within any particular time frame, or even finalized at all, so finalization is fundamentally unsuited to managing limited resources, and especially unsuited to managing resources whose states persist beyond the end of program execution. What, then, could finalization be used for? It has to be something that the program would prefer to happen after an object becomes unreachable, but which it can accommodate never happening. I can only imagine some use related to monitoring the GC process itself, but I reject that as something that the program should not be concerning itself with. I'm open to other suggestions.
> My position is that finalization itself is a fairly "heavy-weight" operation, > and as such is less well-suited to automation. Mainly because there's more to > loose when (or if) the system doesn't handle it as well as I/we could if we > were doing it by hand. (There's a definite parallel there with closing file > handles, which are too heavyweight to be suitable for handling completely > automatically -- by finalisation, for instance ;-) I'll claim that as an argument in favor of my new position against finalization in general.
> The other reason I favour the more explicit approach is that I work with it > often and find it "feels" better (as I've mentioned before, I do more work in [quoted text clipped - 14 lines] > You might say that they form the skeleton of the design, and once you can see > that clearly, then it's easier to see where the flesh fits around it. At least for Java, that's a broken design paradigm. If you need to be certain that resources are released then you cannot depend on finalization to do it. In fact, the place where that paradigm makes the most language-neutral sense is in the context of abnormal program termination, where it might be expected to provide an alternative route for cleaning up resources. In Java, however, that is precisely the scenario where it is _least_ likely that objects will be finalized.
You can in any case provide a very similar feature within Java by placing a mutable "needsToBeFinalized" flag in your classes, and having a finalize method that tests it as a condition for doing anything (else). In that I am now opposing using finalization for anything, I won't advocate building that sort of thing into the language.
> I find it particularly helpful in cases where the object is normally cleaned up > explicitly, but may also be handled by finalisation as a fall-back for when [quoted text clipped - 4 lines] > beFinalizable()/beNotFinalizable() and the whole thing then normally becomes > crystal clear. That sounds like a design issue to me. If there is a complicated cleanup sequence then it ought to be wrapped up in some method. You'll need that anyway if you want the finalizer to be able to clean up properly. If you can wrap it up in a method, then you can invoke that method wherever you might invoke beNotFinalizable(). Am I missing something here?
>>Do you have a concrete-ish example of how your version of finalization >>management would be an improvement over what Java already offers? [quoted text clipped - 9 lines] > would also be unthinkable (for various reasons) not to make open DB connections > clean up by finalisation, where necessary. Notice also that DB code can (in In Smalltalk perhaps that scenario would be unthinkable, but in Java you cannot count on the connection being finalized anyway, so you need to rely on the connection user to clean it up. You _could_ write cleanup code into a finalizer, and that might rescue some broken programs, but it might also disguise the problems in such programs. I would rather have my program fail sooner and more consistently.
> some cases) place considerable strain on the finaliser thread since the code > may not be interactive (so no handy pauses to work in while the user thinks). > The end result of that is that you want to be able to turn off "finalisability" > in the clean-up code, or else you have to write extra code to ensure that the > cleaned-up but still finalisable object puts as little strain on the system as > possible. That sounds like an issue that could be handled by appropriate coding of the finalize() method, if you're going to rely on finalization at all. The difference between Java providing a feature to bypass the finalize() method and the programmer providing a feature to toggle finalize() between "do something" and "do nothing" is insignificant as far as I can see. I'd relegate the feature to the "syntactic sugar" category. Of course, these days it seems that items in that category have a reasonably good chance of being implemented....
> The other example is about managing Windows GDI handles. In this case the > resource to be managed is quite lightweight, and there's no great problem with [quoted text clipped - 6 lines] > of finalisation is just that: it /is/ explicit. It makes it easier to > understand how that part of the system fits together. It seems to me that handling resources such as these demands classes that understand the resources better, rather than classes that require the user to manipulate instances correctly, for some varying definition of "correctly". You might not need distinct classes for "owned" and "shared" handles, but you could at least provide an internal flag to distinguish instances with respect to that characteristic. The objects ought to encapsulate the knowledge of how they need to be cleaned up, and provide a uniform face to users.
So, then, I guess we do have to agree to disagree. I am not persuaded by your examples, and I doubt whether you have been persuaded by my commentary on them. Particularly as you have raised personal preference and style as a contributing factor, I have no reason to expect to change your mind. (And, for the record, I feel no special need to do so.)
John Bollinger jobollin@indiana.edu
Carl Howells - 16 Nov 2004 17:50 GMT > BTW, don't forget that /not/ relying on finalisation can have costs too. More, > and more complicated, code. More opportunity for error. Even, potentially > larger object networks -- e.g. if an object that may need cleanup cannot just > be handed-over to another context and forgotten, then the recieving context may > have to keep a link back to the supplier so that it can "hand back" the object > when it has finished with it. Cleanup of non-memory resources can be done with a PhantomReference and ReferenceQueue combination, with much simpler semantics than those of finalization. After all, there's no chance of object resurrection with that system, meaning that the cleanup *will* only be done once.
It is slightly more difficult to implement than just overriding a finalize method, admittedly... But I think it's also conceptually more elegant.
Oh, and while I'm replying to you, I suspect your interpretation of my test results earlier in this thread was accurate.. Objects which need to be finalized just have to stick around for an extra GC cycle, delaying their collection somewhat, but no real bias exists.
Chris Uppal - 20 Nov 2004 14:21 GMT > Cleanup of non-memory resources can be done with a PhantomReference and > ReferenceQueue combination, with much simpler semantics than those of [quoted text clipped - 4 lines] > finalize method, admittedly... But I think it's also conceptually more > elegant. Fair enough, though I don't share that opinion.
To me it seems like essentially the same thing (introspection on objects' lifetimes) packaged more indirectly (and somewhat more confusingly). I'll admit, though, that that may be more an artefact of Sun's /specific/ design (and poor documentation), than inherent in the idea of reference queues itself. Maybe I'll warm to 'em yet...
-- chris
Stefan Schulz - 09 Nov 2004 18:52 GMT > What else can I do to make sure that cleaner has finished without > freezing my UI. Since you are shutting down anyway, take a look at Runtime.addShutdownHook()
 Signature Whom the gods wish to destroy they first call promising.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|