在类实例垃圾收集之前扫描器#close()调用?

时间:2015-10-02 11:54:48

标签: java memory-leaks garbage-collection

我需要用Iterator编写一个迭代文件的类,并在每次调用Iterator#next()时返回文件的一行。问题是我不知道Iterator实例的引用什么时候会无法访问(并且可能在garbage-collected之后的某个时间),所以我无法调用Scanner#close()

所以问题是,如果在Iterator实例的引用变得无法访问之后但是在垃圾收集之前有任何方法可以调用Scanner#close()

public class FileWrapper implements Iterable<String> {

    private File file;

    @Override
    public Iterator<String> iterator() {
            return new Itr();
    }

    private class Itr implements Iterator<String> {

        private Scanner scanner;

        public Itr() {
            scanner = new Scanner(file);
        }

        @Override
        public boolean hasNext() {
            return scanner.hasNextLine();
        }

        @Override
        public String next() {
            return scanner.nextLine();
        }
    }
}

2 个答案:

答案 0 :(得分:1)

理想的解决方案是制作Iterator工具Closeable,并让iterator()的来电者负责致电close()。可关闭迭代器的close()方法将关闭Scanner

不幸的是,在通过Iterable / Iterator API管理迭代器的生命周期的上下文中,这会产生问题。当然,你将无法获得

 for (String s : someFileWrapper) {
      ....
 }

在循环结束时关闭Iterator

如果您Itr实施AutoCloseable以及Iterator并放弃使用“for each”样式for循环,则可以使用“试用”资源“管理生命周期。但它很麻烦。

另一种可能的解决方案是将文件描述符生命周期的责任从FileWrapper开始;例如使它成为ScannerWrapper,并使生命周期成为创建/管理该对象的任何责任。 (但是,这从根本上改变了包装器的语义。包装器只能用于生成一次迭代器。)

建议的finalize方法实际上并没有实现任何目标。问题是FileInputStream内的Scanner将在ScannerFileWrapper.Itr实例同时变为垃圾。 FileInputStream已经有一个finalize()方法,可以调用close()

答案 1 :(得分:1)

如果你能这样做,我会推荐Closeable方法,因为它是确定性的。

您可以覆盖finalize,但这是非常有问题的。无法保证finalize将被调用。终结者还对垃圾收集器施加惩罚。如果您能找到避免它的方法,请不要弄乱最终化过程。

另外,请注意,此清理将固定到内存中。在内存不足之前,您可能会用完文件句柄。这使得非内存资源的确定性清理成为更好的选择。

还有第三种方法,即使用幻像参考。

public class FileWrapper implements Iterable<String> {

    private File file;
    // Keep track of phantom references to iterators
    private static ReferenceQueue<Itr> references = new ReferenceQueue<>();
    static {
        new Thread(new Runnable() {
            public void run() {
                while(true) {
                    // Block until an iterator is about to be annihilated
                    Reference<Itr> ref = references.remove();
                    Itr aboutToDie = ref.get();
                    try {
                        aboutToDie.scanner.close();
                    }
                    catch(IOException ex) {
                        // Already closed?
                    }
                }
            }
        }).start();
    }

    @Override
    public Iterator<String> iterator() {
        return new Itr();
    }

    private class Itr implements Iterator<String> {

        private Scanner scanner;

        public Itr() throws FileNotFoundException {
            scanner = new Scanner(file);
            synchronized(references) {
                new PhantomReference(scanner, references);
            }
        }

        @Override
        public boolean hasNext() {
            return scanner.hasNextLine();
        }

        @Override
        public String next() {
            return scanner.nextLine();
        }
    }
}

幻影参考有点酷。与强引用或弱引用不同,它们对所指的可用性和可用性有影响,因此幻像引用根本没有指示引用。当所有的强或弱参考都消失并且指示物已经完成时,幻影参考就是在最终毁灭之前剩下的所有参考。

此时,幻像引用将被添加到引用队列中,您可以在该引用队列中拾取以进行预处理。

注意:Google Collections有FinalizablePhantomReference为您管理后台话题。