【问题标题】:What guarantees does Java/Scala make about garbage collection?Java/Scala 对垃圾收集有什么保证?
【发布时间】:2014-11-01 03:03:20
【问题描述】:

Play 框架有 play.api.libs.Files.TemporaryFile,它持有对 File 的引用,并在 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()
  }

}

我知道这有一些问题,例如,您可以填满整个磁盘而 JVM 感觉不需要 GC。


但这里我问的是程序的“正确性”,即没有磁盘空间限制的程序。

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

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

在我从文件中读取之前可以删除 /foo/bar 吗?我在// last use之后不使用tempFile,那之后可以马上定稿吗?

或者如果它作为参数传递给函数呢?

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()
  }
}

如果在上面的示例中,tempFile 可能在我使用完毕之前被删除,TemporaryFile 的正确用法是什么,这样就不会发生这种情况?

【问题讨论】:

  • 请注意,读取文件和File 实例之间没有任何关系,除了用于标识文件系统资源的名称。
  • @SotiriosDelimanolis,正确。
  • 好问题。并且没有一个答案解决了在已经收集了 TemporaryFile 对象的情况下处理异步 Future 块中的 File 引用时可能发生的问题......我怀疑这会在我的应用程序上产生一个错误!

标签: java scala garbage-collection playframework-2.3


【解决方案1】:

一旦您不再拥有对该对象的强引用,Java 对象就有资格进行垃圾回收。这不取决于您是否“使用”该对象。

在这个例子中,

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 不符合垃圾回收条件,因此在不再使用 foo() 之前完成。使用来自tempFile 的成员的对象可能会使用它,并使其不符合条件的时间超过foo() 中的最后一次使用时间。

在这个例子中,

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()
  }
}

结果是一样的。

在一个小的变体中(Java,我不太了解 Scala 语法),

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. 

【讨论】:

  • @Dici 我的意思是您的标准票价参考,没有多余的参考。弱引用是阻止 GC 执行其业务的引用。见docs.oracle.com/javase/8/docs/api/java/lang/ref/…
  • Java中有4种引用,强、软、弱、幻。进一步阅读:rallydev.com/community/engineering/…。基本强防止对象被GC
  • tempFile 不符合垃圾回收的条件,因此在 foo() 返回之前完成 是错误的。由于在访问其file 字段后未使用tempFile,因此它引用的对象在该访问后立即变为合格。
  • @SotiriosDelimanolis 我不明白,tempFile 在方法返回之前是可以访问的。
【解决方案2】:

在执行退出其范围之前,局部变量不会被最终确定。 GC 不会终止您不使用的对象,它会终止您无法再访问的对象,因此在您使用 tempFile 的示例中,它不会在 foo() 调用之前发生完成了。

【讨论】:

  • 作用域与 GC 关系不大。范围定义了您可以在何处使用某些实体的名称。即使包含局部变量的方法(或其他范围)尚未结束,局部变量引用的对象也可以被 GC'ed。
  • 我的意思是局部变量不能在之前执行退出其范围,因为在这种情况下它显然是可访问的。但是,我确实知道在本地创建的对象在包含它的方法结束后可以继续存在。它更清楚,还是我应该更正我的答案?
  • 我是说局部变量引用的对象可以在执行退出其(局部变量)范围之前死亡。 Scope 只处理程序中名称使用的有效性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
  • 2015-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多