Java 在类实例垃圾回收之前进行Scanner#close()调用?

Java 在类实例垃圾回收之前进行Scanner#close()调用?,java,memory-leaks,garbage-collection,Java,Memory Leaks,Garbage Collection,我需要使用迭代器编写一个类,该类在每次调用Iterator\next()时对文件进行迭代并返回文件的一行。问题是,我不知道迭代器实例的引用何时将变得不可访问(可能在一段时间后垃圾收集),因此我无法调用Scanner\close() 所以问题是,在迭代器实例的引用变得不可访问之后,但在它被垃圾收集之前,是否有任何方法调用Scanner#close() public class FileWrapper implements Iterable<String> { private

我需要使用迭代器编写一个类,该类在每次调用
Iterator\next()
时对文件进行迭代并返回文件的一行。问题是,我不知道迭代器实例的引用何时将变得不可访问(可能在一段时间后
垃圾收集
),因此我无法调用
Scanner\close()

所以问题是,在迭代器实例的引用变得不可访问之后,但在它被垃圾收集之前,是否有任何方法调用
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();
        }
    }
}
公共类文件包装器实现了Iterable{
私有文件;
@凌驾
公共迭代器迭代器(){
返回新的Itr();
}
私有类Itr实现迭代器{
私人扫描仪;
公共资讯科技署(){
扫描仪=新扫描仪(文件);
}
@凌驾
公共布尔hasNext(){
返回scanner.hasNextLine();
}
@凌驾
公共字符串next(){
返回scanner.nextLine();
}
}
}

理想的解决方案是使
迭代器
也实现
可关闭
,并使
迭代器()的调用方负责调用
关闭()
。可关闭迭代器的
close()
方法将关闭
扫描仪

不幸的是,把它塞进一个上下文中,迭代器的生命周期是通过
Iterable
/
iterator
api来管理的。。。。。这将是个问题。当然,你不可能

 for (String s : someFileWrapper) {
      ....
 }
关闭循环结束时的
迭代器

如果您使
Itr
实现
AutoCloseable
以及
Iterator
并放弃使用“for-each”样式
for
循环,您可以使用“try-with-resources”来管理生命周期。但这很麻烦


另一个可能的解决方案是从
文件包装器
中分离出对文件描述符生命周期的责任;e、 g.将其改为
扫描包装器
,并使生命周期由创建/管理该对象的人负责。(然而,这从根本上改变了包装器的语义。包装器只能用于生成一次迭代器。)



建议的
finalize
方法实际上没有取得任何效果。问题是
扫描仪
内的
文件输入流
将与
扫描仪
文件包装器.Itr
实例同时成为垃圾
FileInputStream
已经有了一个
finalize()
方法,该方法将调用
close()

如果可以的话,我建议您使用
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();
        }
    }
}
公共类文件包装器实现了Iterable{
私有文件;
//跟踪对迭代器的虚拟引用
private static ReferenceQueue references=new ReferenceQueue();
静止的{
新线程(newrunnable()){
公开募捐{
while(true){
//直到迭代器即将被消除为止
Reference ref=references.remove();
Itr aboutodie=ref.get();
试一试{
aboutodie.scanner.close();
}
捕获(IOEX异常){
//已经关门了?
}
}
}
}).start();
}
@凌驾
公共迭代器迭代器(){
返回新的Itr();
}
私有类Itr实现迭代器{
私人扫描仪;
public Itr()引发FileNotFoundException{
扫描仪=新扫描仪(文件);
已同步(参考){
新幻影参考(扫描仪、参考);
}
}
@凌驾
公共布尔hasNext(){
返回scanner.hasNextLine();
}
@凌驾
公共字符串next(){
返回scanner.nextLine();
}
}
}
幻影参考有点酷。与强引用或弱引用不同(强引用或弱引用会影响到要收集的引用对象的可达性和可用性),幻象引用对引用对象没有任何控制。当所有的强引用或弱引用都消失了,引用对象被最终确定时,幻影引用就是最终湮灭之前剩下的全部内容

此时,幻影引用将被添加到引用队列中,您可以从中提取该引用以执行验尸前处理


注意:Google Collections为您管理后台线程。

您可以尝试在
Itr
类的
finalize
方法中添加它(除非我误解了什么)。您还可以将您的
Itr
设置为可关闭的
,并让用户关闭它(或尝试使用资源)。