Scoping of Anonymous Instances
Colour me naive. I honestly believed that anonymous instances (ie. ones instantiated, but never assigned to a variable) would be scoped to live for the entire method invocation they where created in. But that’s not the case. Consider the following Java program:
class AnonymousScopes {
protected void finalize () {
System.out.println ("FINALIZED!");
}
public static void main (String[] args) {
new AnonymousScopes ();
String[] array = new String[100];
for (int i = 0; i < array.length; i++) {
array[i] = Integer.toString (i);
System.gc();
}
System.out.println("DONE");
}
}
If you compile it and run it you will actually see:
$ java AnonymousScopes FINALIZED! DONE
That is the AnonymousScopes instance created on the first line of main() is garbage collected before main returns. Thanks to Jürg for teaching me Java Programming 101
And for Gnomies using Vala – yes – Vala behaves the same way.
June 16th, 2010 at 7:39 am
I think this is really clever for the JVM, b/c there is no need to allocate memory if surely r not going to use that reference ahead in your method. For the interpreter it’s easy just b/c u don’t assign the class to anything so is impossible u gonna use it again.
Btw, if u r learning java u must know that rarely it’s a good practice the need to call a constructor and to don’t use the created instance.
June 16th, 2010 at 10:42 am
@deiga: I actually did professional Java for three years, so I don’t have the excuse of being entirely green here
The “anonymous instance pattern” (or what we should call it) is commonly used if AnonymousScoping spins of a non-daemon thread in the constructor. That will prevent the JVM from exiting when main() returns. Consider fx. if AnonymousScoping was some kind of RPC server, a natural main() implementation will simply instantiate the server object (without a ref) and return.
At least that’s how my mind works…
June 16th, 2010 at 12:39 pm
I think you are mixing scope and garbage collection. Also the behavior you are seeing is not guaranteed. Let me see if I can help you out.
First if you are talking about scope, you are talking about the scope of a reference variable which is created on the stack. In the example you show you have two reference variables (array and i). array is scoped from declaration to the end of the main method, while i is scoped within the for loop.
Next we have the actual objects that are created. All objects live on the heap. In your sample you have an Array object, the AnonymousScopes object, and a handful of String objects (Strings are special, so normal Garbage Collection doesn’t apply to them).
The (short) rule for Garbage Collection is that an object is available for GC as soon as it is not referenced from any live thread. In your code you have created a new object on the heap, the AnonymousScopes instance, but you never assign it to a reference which makes it immediately available for garbage collection.
Now here is the part that is not guaranteed. As I said the unreferenced object is “available” for garbage collection, but that does not mean it will immediately be garbage collected. The object may in fact not be garbage collected until the end of the program.
So this behavior really has nothing to do with scope, and all to do with GC.
June 16th, 2010 at 3:32 pm
Don’t rely on that behaviour – like Steven said,it’s not guaranteed. JVM spec requires that finalize() be called when garbage collected – however that could happen at any time after the object is no longer referenced.
Further, calling System.gc() doesn’t guarantee anything either. That method is nothing more than a hint to the JVM that now would be a good time to spend some effort on garbage collection – the JVM may ignore it, or may be configured to disallow explicit gc() calls (app servers will often do this).
June 17th, 2010 at 12:40 am
@Steven: I think you are right. I was subconsciously mixing scope and gc – probably because they are (almost) equivalent when you declare a variable to hold the ref. My bewilderment was also strengthened since I have been using anonymous instances lots of places without any problems (like I describe in my comment to deiga), but the crucial point for all the cases where I’ve used it is that I’ve spun off a non-daemon thread from the anonymous instance.
@Simon: I agree on all accounts. I am not relying on that behaviour – more expecting the opposite
And the System.gc() call was merely to make sure I got the (un)expected behaviour.
June 17th, 2010 at 3:09 am
Even if you had assigned it to a variable (e.g. “AnonymousScopes as = new AnonymousScopes ();”) it may not help, since I’m fairly certain the JVM is allowed to realise you don’t actually use it via escape analysis. In fact in certain situations the JVM can choose to not even allocate it on the heap since it knows no reference escapes.
If something needs the object to still be alive, that something should have a reference to it.
The only guarantee about finalize() is that it will be called before any PhantomReferences are enqueued – and that may be _never_ (the JVM can exit without them being run). The only time finalize() should ever be used it to release native non-Java resources, since it can cause larne number of objects to be retained in memory un-necessarily.
June 17th, 2010 at 3:00 pm
@Kamstrup: “I was subconsciously mixing scope and gc – probably because they are (almost) equivalent when you declare a variable to hold the ref.”
And you’re still mixing them, because they’re *not* almost equivalent. When the variable goes out of scope, the reference to object is gone, making the object *eligible* for garbage collection. That’s very emphatically not the same thing as causing it to *be* garbage collected, which might happen ten minutes after the variable went out of scope.
(In practice, most JVMs are smart enough to recognise short-lived objects, and deal with them accordingly. But again, that’s an implementation detail, which should not be assumed.)