什么时候Java本地变量符合GC的条件?

Dav*_*ths 5 java

鉴于以下计划:

import java.io.*;
import java.util.*;

public class GCTest {

    public static void main(String[] args) throws Exception {
        List cache = new ArrayList();
        while (true) {
            cache.add(new GCTest().run());
            System.out.println("done");
        }
    }

    private byte[] run() throws IOException {
        Test test = new Test();
        InputStream is = test.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buff = new byte[256];
        int len = 0;
        while (-1 != (len = is.read())) {
            baos.write(buff, 0, len);
        }
        return baos.toByteArray();
    }

    private class Test {
        private InputStream is;

        public InputStream getInputStream() throws FileNotFoundException {
            is = new FileInputStream("GCTest.class");
            return is;
        }

        protected void finalize() throws IOException {
            System.out.println("finalize");
            is.close();
            is = null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当run方法中的while循环仍在执行且局部变量test仍在范围内时,你会期望调用finalize吗?

更重要的是,这种行为是否在任何地方定义?Sun有什么说它是实现定义的吗?

这与之前在SO上提出问题的方式相反,人们主要关心的是内存泄漏问题.在这里,我们让GC积极地GCing我们仍然感兴趣的变量.你可能会期望,因为测试仍在"范围内",它不会是GC.

对于记录,似乎有时测试"工作"(即最终命中OOM),有时它会失败,具体取决于JVM实现.

没有捍卫这个代码编写BTW的方式,这只是一个工作中出现的问题.

Yis*_*hai 14

虽然如果对象仍然在范围内,它将不会被垃圾收集,但如果变量实际上没有在代码中进一步使用(因此您看到的行为不同),JIT编译器可能会将其从范围中取出,即使你阅读源代码,变量似乎仍然"在范围内".

我不明白为什么你关心如果一个对象被垃圾收集,如果你不再在代码中引用它,但如果你想确保对象留在内存中,最好的方法是直接在类的字段中引用它们,甚至在静态领域更好.如果静态字段引用该对象,则不会收集垃圾.

编辑:是您正在寻找的明确文档.

>我假设一个对象本地引用超出范围之前不能死.

这不能假设.Java规范和JVM规范都不能保证这一点.

仅仅因为变量在范围内,并不意味着它指向的对象是可达的.通常情况下,范围内变量指向的对象是可访问的,但是您的情况不是这样.编译器可以在jit时确定哪些变量已经死亡,并且在oop-map中不包含这些变量.由于"nt"指向的对象可以从任何实时变量到达[sic - 应该是不能],因此它有资格进行收集.

  • @Dave:那个人错了.干净利落.JLS和JVM规范不保证"不可见"引用不会被垃圾回收. (6认同)
  • 请记住,Hotspot VM比StackOverflow上的每个人都更聪明. (5认同)

Ste*_*n C 8

我建议你和你的同事阅读关于垃圾收集的真相.

在一开始,它说:

Java平台的规范很少有关于垃圾收集实际工作方式的承诺.[消隐]

虽然看起来很混乱,但垃圾收集模型没有严格定义这一事实实际上是重要且有用的 - 严格定义的垃圾收集模型可能无法在所有平台上实现.同样,它可能会排除有用的优化并长期损害平台的性能.

在您的示例中,test变量在while循环中变为"不可见"(参见上面的A.3.3).此时,一些JVM将继续将变量视为包含"硬引用",而其他JVM将视为变量已被置零.对于兼容的JVM,这两种行为都是可接受的

引自JLS第3版(第12.6.1节第2段):

可到达对象是可以从任何活动线程的任何潜在继续计算中访问的任何对象.

请注意,可访问性根本没有根据范围定义.引用文本如下:

可以设计优化程序的转换,以减少可达到的对象的数量,使其少于可以被认为可达的对象的数量. 例如,编译器或代码生成器可以选择设置将不再用于null的变量或参数,以使得此类对象的存储可能更快地被回收.

(我的重点补充说.)这意味着对象对象可能被垃圾收集,并且最终化可能比您预期的更早或更晚发生.值得注意的是,在完成无法访问的对象之前,某些JVM需要多个GC周期.

最重要的是,依赖于早期或晚期发生的终结的程序本质上是不可移植的,并且在我看来是错误的.