John (& all),
Sorry I didn't jump into this thread earlier, but the s:n on this list
has pushed it down my reading priority list ... way down.
John's understanding of finalizer and runFinalizersOnExit as I've
understood him to state it in this thread matches my own interpretation of the
Java Language spec and JVM spec in this regard. If runFinalizersOnExit is set
then the finalizer must be called; come hell or high water, that code should
be given the opportunity to run at some point. It could be when the GC needs to
purge memory inorder to allocate a request or by explicit call to the API
java/lang/Runtime.gc()V, or when the runtime is exiting. Note that if the gc
doesn't have to purge memory to satisfy a request I don't believe it will
finalize an object, put it on the queue maybe, but not actually do it.
I don't have my hands on a functional Linux box for a few days, so I
tested this on Sun's 1.1.6N reference implementation on WinNT. Can someone
else (John?) run these tests on the blackdown port? Inorder to really test
this we need to trace the method dispatching. To do this we need to run with
the debugable build of the java runtime, java_g (jre_g does not allow method
tracing????) and we need to flip the switch to turn on the method tracing with
-tm on command line, or the java/lang/Runtime.traceMethodCalls(Z)V API. The
command line switch is tedious in that you have to wade through all the
runtime initialization calls, which aren't very interesting to us so I'll go
with the API.
I've modified John's posted testcase code as follows:
--begin F.java--
00:public class F
01:{
02: static //make this code part of "F.<clinit>()V"
03: {
04: Runtime.getRuntime().traceMethodCalls(true); //activate method dispatch trace
05: }
06: protected void finalize() throws Throwable
07: {
08:// System.out.println("finalize"); //took too much time, no longer needed
09: super.finalize();
10: }
11: public static void main(String[] args) throws Throwable
12: {
13: F f = new F();
14:// System.runFinalizersOnExit(true); //redundant
15: Runtime.getRuntime().runFinalizersOnExit(true);
16: f = null;
17:// System.gc(); //not needed
18: }
19:}
--end F.java--
Then ran it: (all lines begining with '#' are the output, those with '>'
are my comments to explain what's happening for those unfamiliar with this
level of the JVM.)
E:\TEMP\New Folder>javac F.java
E:\TEMP\New Folder>java_g F
# main [ 1] | < java/lang/Runtime.traceMethodCalls(Z)V returning
# main [ 0] < F.<clinit>()V returning
>class init & therefore load has just finished, so now start the actual pgm
# main [ 0] > F.main([Ljava/lang/String;)V (1) entered
>gotta construct an instance first
# main [ 1] | > F.<init>()V (1) entered
>superclass ctor
# main [ 2] | | > java/lang/Object.<init>()V (1) entered
# main [ 2] | | < java/lang/Object.<init>()V returning
# main [ 1] | < F.<init>()V returning
>line 15
# main [ 1] | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 1] | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
>still line 15
# main [ 1] | > java/lang/Runtime.runFinalizersOnExit(Z)V (1) entered
>within the code for runFinalizersOnExit
> first check to see if the SecurityManager will let you toggle this
# main [ 2] | | > java/lang/System.getSecurityManager()Ljava/lang/SecurityManager; (0)
entered
# main [ 2] | | < java/lang/System.getSecurityManager()Ljava/lang/SecurityManager;
returning
>apparently it will... so call the native code that actually toggles it
# main [ 2] | | > java/lang/Runtime.runFinalizersOnExit0(Z)V (1) entered
# main [ 2] | | < java/lang/Runtime.runFinalizersOnExit0(Z)V returning
# main [ 1] | < java/lang/Runtime.runFinalizersOnExit(Z)V returning
# main [ 0] < F.main([Ljava/lang/String;)V returning
>that's the end of main, so begin the runtime exit processes
> finalize all objects still live
# main [ 0] > F.finalize()V (1) entered
>super.finalize() call on line 9
# main [ 1] | > java/lang/Object.finalize()V (1) entered
# main [ 1] | < java/lang/Object.finalize()V returning
# main [ 0] < F.finalize()V returning
>now finalize System objects, start with System.in
# main [ 0] > java/io/FileInputStream.finalize()V (1) entered
# main [ 0] < java/io/FileInputStream.finalize()V returning
>then System.out
# main [ 0] > java/io/FileOutputStream.finalize()V (1) entered
# main [ 1] | > java/io/OutputStream.flush()V (1) entered
# main [ 1] | < java/io/OutputStream.flush()V returning
# main [ 0] < java/io/FileOutputStream.finalize()V returning
>and System.err
# main [ 0] > java/io/FileOutputStream.finalize()V (1) entered
# main [ 1] | > java/io/OutputStream.flush()V (1) entered
# main [ 1] | < java/io/OutputStream.flush()V returning
# main [ 0] < java/io/FileOutputStream.finalize()V returning
>that's all
E:\TEMP\New Folder>
You should get the same sequence of events on all JVMs. Now to
play with it a little I commented out line 16 - the null'ing of f.
Why? Because that should not need to be done.
# main [ 1] | < java/lang/Runtime.traceMethodCalls(Z)V returning
# main [ 0] < F.<clinit>()V returning
# main [ 0] > F.main([Ljava/lang/String;)V (1) entered
# main [ 1] | > F.<init>()V (1) entered
# main [ 2] | | > java/lang/Object.<init>()V (1) entered
# main [ 2] | | < java/lang/Object.<init>()V returning
# main [ 1] | < F.<init>()V returning
# main [ 1] | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 1] | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
# main [ 1] | > java/lang/Runtime.runFinalizersOnExit(Z)V (1) entered
# main [ 2] | | > java/lang/System.getSecurityManager()Ljava/lang/SecurityManager; (0)
entered
# main [ 2] | | < java/lang/System.getSecurityManager()Ljava/lang/SecurityManager;
returning
# main [ 2] | | > java/lang/Runtime.runFinalizersOnExit0(Z)V (1) entered
# main [ 2] | | < java/lang/Runtime.runFinalizersOnExit0(Z)V returning
# main [ 1] | < java/lang/Runtime.runFinalizersOnExit(Z)V returning
# main [ 0] < F.main([Ljava/lang/String;)V returning
# main [ 0] > F.finalize()V (1) entered
# main [ 1] | > java/lang/Object.finalize()V (1) entered
# main [ 1] | < java/lang/Object.finalize()V returning
# main [ 0] < F.finalize()V returning
# main [ 0] > java/io/FileInputStream.finalize()V (1) entered
# main [ 0] < java/io/FileInputStream.finalize()V returning
# main [ 0] > java/io/FileOutputStream.finalize()V (1) entered
# main [ 1] | > java/io/OutputStream.flush()V (1) entered
# main [ 1] | < java/io/OutputStream.flush()V returning
# main [ 0] < java/io/FileOutputStream.finalize()V returning
# main [ 0] > java/io/FileOutputStream.finalize()V (1) entered
# main [ 1] | > java/io/OutputStream.flush()V (1) entered
# main [ 1] | < java/io/OutputStream.flush()V returning
# main [ 0] < java/io/FileOutputStream.finalize()V returning
Looks the same to me. What if the runFinalizersOnExit flag is unset (the default)?
Commented out line 15 as well.
# main [ 1] | < java/lang/Runtime.traceMethodCalls(Z)V returning
# main [ 0] < F.<clinit>()V returning
# main [ 0] > F.main([Ljava/lang/String;)V (1) entered
# main [ 1] | > F.<init>()V (1) entered
# main [ 2] | | > java/lang/Object.<init>()V (1) entered
# main [ 2] | | < java/lang/Object.<init>()V returning
# main [ 1] | < F.<init>()V returning
# main [ 0] < F.main([Ljava/lang/String;)V returning
Well, as expected, the finalizer was not run. Now a quick look at the
_other_ way the finalizer can get activated... via the gc. Here I've
explicitly called it, but it could also have been called implicitly
to handle an allocation failure... I've modified main as follows:
11:public static void main(String[] args) throws Throwable
12:{
13: F f = new F();
14: System.runFinalizersOnExit(true);
15: Runtime.getRuntime().gc(); //give it a chance to put anything on queue
16: Runtime.getRuntime().runFinalization(); //purge queue
17: Thread.sleep(10000); //make sure f is NOT null'd before fin thread finishes
18: f = null; //throw it away
19: Runtime.getRuntime().runFinalization(); //see if that put it on queue
20: Runtime.getRuntime().gc(); //this had better put f onto queue!!
21: Runtime.getRuntime().runFinalization(); //now purge it
22: Thread.sleep(10000); //wait before letting system exit to allow queue to finish
23:}
and here are the results:
E:\TEMP\New Folder>java_g F
# main [ 1] | < java/lang/Runtime.traceMethodCalls(Z)V returning
# main [ 0] < F.<clinit>()V returning
# main [ 0] > F.main([Ljava/lang/String;)V (1) entered
# main [ 1] | > F.<init>()V (1) entered
# main [ 2] | | > java/lang/Object.<init>()V (1) entered
# main [ 2] | | < java/lang/Object.<init>()V returning
# main [ 1] | < F.<init>()V returning
# main [ 1] | > java/lang/System.runFinalizersOnExit(Z)V (1) entered
# main [ 2] | | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 2] | | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
# main [ 2] | | > java/lang/Runtime.runFinalizersOnExit(Z)V (1) entered
# main [ 3] | | | > java/lang/System.getSecurityManager()Ljava/lang/SecurityManager;
(0) entered
# main [ 3] | | | < java/lang/System.getSecurityManager()Ljava/lang/SecurityManager;
returning
# main [ 3] | | | > java/lang/Runtime.runFinalizersOnExit0(Z)V (1) entered
# main [ 3] | | | < java/lang/Runtime.runFinalizersOnExit0(Z)V returning
# main [ 2] | | < java/lang/Runtime.runFinalizersOnExit(Z)V returning
# main [ 1] | < java/lang/System.runFinalizersOnExit(Z)V returning
# main [ 1] | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 1] | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
# main [ 1] | > java/lang/Runtime.gc()V (1) entered
# main [ 1] | < java/lang/Runtime.gc()V returning
# main [ 1] | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 1] | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
# main [ 1] | > java/lang/Runtime.runFinalization()V (1) entered
# main [ 1] | < java/lang/Runtime.runFinalization()V returning
# main [ 1] | > java/lang/Thread.sleep(J)V (2) entered
>if there was anything on finalization queue we would see it being finalized before now
# main [ 1] | < java/lang/Thread.sleep(J)V returning
>f got nulled out here
# main [ 1] | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 1] | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
# main [ 1] | > java/lang/Runtime.runFinalization()V (1) entered
# main [ 1] | < java/lang/Runtime.runFinalization()V returning
> if it was on the queue we'd see it here
# main [ 1] | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 1] | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
# main [ 1] | > java/lang/Runtime.gc()V (1) entered
# main [ 1] | < java/lang/Runtime.gc()V returning
>it should have just been added to queue
# main [ 1] | > java/lang/Runtime.getRuntime()Ljava/lang/Runtime; (0) entered
# main [ 1] | < java/lang/Runtime.getRuntime()Ljava/lang/Runtime; returning
# main [ 1] | > java/lang/Runtime.runFinalization()V (1) entered
>yupers! here it is...
# Finalizer thread [ 0] > F.finalize()V (1) entered
# Finalizer thread [ 1] | > java/lang/Object.finalize()V (1) entered
# Finalizer thread [ 1] | < java/lang/Object.finalize()V returning
# Finalizer thread [ 0] < F.finalize()V returning
# main [ 1] | < java/lang/Runtime.runFinalization()V returning
# main [ 1] | > java/lang/Thread.sleep(J)V (2) entered
# main [ 1] | < java/lang/Thread.sleep(J)V returning
# main [ 0] < F.main([Ljava/lang/String;)V returning
# main [ 0] > java/io/FileInputStream.finalize()V (1) entered
# main [ 0] < java/io/FileInputStream.finalize()V returning
# main [ 0] > java/io/FileOutputStream.finalize()V (1) entered
# main [ 1] | > java/io/OutputStream.flush()V (1) entered
# main [ 1] | < java/io/OutputStream.flush()V returning
# main [ 0] < java/io/FileOutputStream.finalize()V returning
# main [ 0] > java/io/FileOutputStream.finalize()V (1) entered
# main [ 1] | > java/io/OutputStream.flush()V (1) entered
# main [ 1] | < java/io/OutputStream.flush()V returning
# main [ 0] < java/io/FileOutputStream.finalize()V returning
So it looks like Finalizer works just like John and I tought it did. If these
aren't the results you get on blackdown then I for one would say it's a defect.
Sorry to have rambled. Thanks for reading this far. -=Chris
<*> cabbey at rconnect dot com http://homepage.rconnect.com/cabbey/ <*>
Get it up, keep it up... LINUX: Viagra for the PC. ;) PGP Info: pgp.html
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12 http://www.geekcode.com
GCS$/IT/PA$ d(-) s++:+ a-- C+++$ UL++++ UA++$ P++ L++ E- W++ N+ o? K? !P
w---(+)$ O- M-- V-- Y+ PGP+ t--- 5++ X+ R tv b+ DI+++ D G e++ h(+) r@ y?
------END GEEK CODE BLOCK------