Search code examples
javamemory-leaksgarbage-collectionjvmsun

Are invisible references still an issue in recent JVMs?


I was reading Java Platform Performance (sadly the link seems to have disappeared from the internet since I originally posed this question) and section A.3.3 worried me.

I had been working on the assumption that a variable that dropped out of scope would no longer be considered a GC root, but this paper appears to contradict that.

Do recent JVMs, in particular Sun's 1.6.0_07 version, still have this limitation? If so, then I have a lot of code to analyse...

I ask the question because the paper is from 1999 - sometimes things change, particularly in the world of GC.


As the paper is no longer available, I'd like to paraphrase the concern. The paper implied that variables that were defined inside a method would be considered a GC root until the method exited, and not until the code block ended. Therefore setting the variable to null was necessary to permit the Object referenced to be garbage collected.

This meant that a local variable defined in a conditional block in the main() method (or similar method that contained an infinite loop) would cause a one-off memory leak unless you nulled a variable just before it dropped out of scope.

The code from the chosen answer illustrates the issue well. On the version of the JVM referenced in the document, the foo object can not be garbage collected when it drops out of scope at the end of the try block. Instead, the JVM will hold open the reference until the end of the main() method, even though it is impossible for anything to use that reference.

This appears to be the origin of the idea that nulling a variable reference would help the garbage collector out, even if the variable was just about to drop out of scope.


Solution

  • This code should clear it up:

    public class TestInvisibleObject{
      public static class PrintWhenFinalized{
        private String s;
        public PrintWhenFinalized(String s){
          System.out.println("Constructing from "+s);
          this.s = s;
        }
        protected void finalize() throws Throwable {
          System.out.println("Finalizing from "+s);
        }   
      }
      public static void main(String[] args) {
        try {
            PrintWhenFinalized foo = new PrintWhenFinalized("main");
        } catch (Exception e) {
            // whatever
        }
        while (true) {
          // Provoke garbage-collection by allocating lots of memory
          byte[] o = new byte[1024];
        } 
      }
    }
    

    On my machine (jdk1.6.0_05) it prints:

    Constructing from main

    Finalizing from main

    So it looks like the problems has been fixed.

    Note that using System.gc() instead of the loop does not cause the object to be collected for some reason.