Garbage collection on a local variable
I'm a C++ programmer entering the world of Java. And I cannot get rid of the bad feeling of having to let the Java garbage collector do my cleaning.
How, for example, will this code behave in Java?
public void myFunction() {
myObject object = new myObject();
object.doSomething();
}
Will the local variable object be deleted when myFunction() exits?
Do I have to set object to null before exiting, or will it be out of scope and be deleted by the GC? Or, at worst, will it leak like it would in C++?
It will be garbage collected at some point after it's no longer used. I believe in current implementations of Java it will actually persist until the end of the method, whereas the garbage collector in .NET is more aggressive. (I don't know whether there are any guarantees even in Java. Normally you'd only want the local variable to persist beyond its last possible read when you're debugging.)
But no, you don't need to set the variable to null, and doing so would harm readability.
It's unlikely that the object will garbage collected immediately after the method exits; it's up to when the GC runs... and of course if anything else holds onto a reference to the object, it may not be eligible for garbage collection anyway. Don't forget that the value of the variable is just a reference, not the object itself. (That may take a while to get used to coming from C++.)
GC does the job at least when memory limit approaches, since any object referenced by out-of-scope variables can be garbage collected, but there is an unexpected behavior of out-of-scope local variables of the last exited block.
To look under the hood, let us compare :
{
final List myTooBigList = new ArrayList();
... overfill the list
}
somethingRunOutOfMemory();
somethingRunOutOfMemory()
because myTooBigList
were not GCable, despite not in scope anymore and more memory was claimed, unlike the following :
{
final List myTooBigList = new ArrayList();
... overfill the list
}
Object fake = null;
somethingDoesNotRunOutOfMemory();
Affectation of fake
moves the stack pointer back and let myTooBigList
GCable, then GC does its work as soon as memory limit approaches.
The surprise is that (at least in the jvm I'm testing) we have to explicitely reuse stack at the same level. It would be expected that local variables be GCable as soon as the block is exited, but I guess it's a compromise with performance. It would complicate much the bytecode.
Like in C, local variables are located in stack beside frames. The stack pointer reserves as much space as required for the local variables in scope. Local variable of an { exited block } become GCable when stack is reused, that is: after a local variable is declared at the same stack level, after the function returns, or after an exit evaluation (catch, loop condition).
They are GCable after :
try { } catch : after exit by catch because catch reuses stack
for { } : after exit loop condition because evaluation reuses stack
while { } : after exit loop condition because evaluation reuses stack
{ } declare=value : after any declaration+affectation that reuses stack
They are not GCable just after :
try { } : after nothing caught
for { } : after exit by break
while { } : after exit by break
do { }
if { }
{ }
NOTE : for a lab, run GC then compare a WeakReference(my variable)
to null
.
final WeakReference gctest;
{
final List myTooBigList = new ArrayList();
gctest = new WeakReference(myTooBigList);
... overfill the list
}
Object fake = null;
System.gc();
assert gctest.get() == null;