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
设置为可关闭的
,并让用户关闭它(或尝试使用资源)。