Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What guarantees does Java/Scala make about garbage collection?

The Play framework has play.api.libs.Files.TemporaryFile that holds a reference to a File, and deletes it in the TemporaryFile#finalize().

case class TemporaryFile(file: File) {

  def clean(): Boolean = {
    file.delete()
  }

  def moveTo(to: File, replace: Boolean = false) {
    Files.moveFile(file, to, replace = replace)
  }

  override def finalize {
    clean()
  }

}

I know that there are some issues with this, for example, you could fill up the entire disk without the JVM feeling the need to GC.


But here I ask about the "correctness" of a program, i.e. a program with no limit on disk space.

def foo() {
    val tempFile = TemporaryFile(new File("/tmp/foo"))

    val inputStream = new FileInputStream(tempFile.file) // last use
    try {
        println(inputStream.read())
    } finally {
        inputStream.close()
    }
}

Could /foo/bar be deleted before I've read from the file? I don't use tempFile after // last use, so could it be finalized immediately after that?

Or what if it is passed as an argument to a function?

def foo() {
  val tempFile = TemporaryFile(new File("/tmp/foo"))
  bar(tempFile)
}

def bar(tempFile: TemporaryFile) {
  val inputStream = new FileInputStream(tempFile.file) // last use
  try {
      println(inputStream.read())
  } finally {
      inputStream.close()
  }
}

If in the example above, tempFile may be removed before I am done using it, what is the correct use of TemporaryFile so that this does not happen?

like image 516
Paul Draper Avatar asked Mar 19 '23 13:03

Paul Draper


1 Answers

Java objects are eligible for garbage collection once you no longer has a strong reference to the object. This is not dependent on if you "use" the object or not.

In this example,

def foo() {
    val tempFile = TemporaryFile(new File("/tmp/foo"))

    val inputStream = new FileInputStream(tempFile.file) // last use
    try {
        println(inputStream.read())
    } finally {
        inputStream.close()
    }
}

tempFile is not eligible for garbage collection, and therefore finalization, until foo() is is no longer used. It's possible that objects that use members from tempFile may use it, and keep it ineligible longer than the last use inside foo().

In this example,

def foo() {
  val tempFile = TemporaryFile(new File("/tmp/foo"))
  bar(tempFile)
}

def bar(tempFile: TemporaryFile) {
  val inputStream = new FileInputStream(tempFile.file) // last use
  try {
      println(inputStream.read())
  } finally {
      inputStream.close()
  }
}

The result is the same.

In a minor variant (Java, I don't know Scala syntax well),

class Foo {
    List<Object> objects = new List<Object>(); 
    void foo(Object o) { 
        objects.add(o); 
    }
}

// ...

Foo f = new Foo(); 
f.foo(new Object()); // The object we just created is not eligible for garbage 
                     // collection until the `Foo f` is not used, because
                     // it holds a strong reference to the object. 
like image 73
jdphenix Avatar answered Apr 25 '23 23:04

jdphenix