Java/Scala 对垃圾收集有啥保证?

Posted

技术标签:

【中文标题】Java/Scala 对垃圾收集有啥保证?【英文标题】: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 引用时可能发生的问题......我怀疑这会在我的应用程序上产生一个错误! 【参考方案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 ***.com/questions/9809074/… @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 只处理程序中名称使用的有效性。

以上是关于Java/Scala 对垃圾收集有啥保证?的主要内容,如果未能解决你的问题,请参考以下文章

面试官:谈谈你对G1垃圾收集器都有哪些了解?

JVM垃圾收集之可达性分析

java垃圾回收

java垃圾回收

2. Java中的垃圾收集 - GC参考手册

标记为垃圾收集vs符合java中的垃圾收集条件