Java 为什么我的BufferedReader代码会泄漏内存?

Java 为什么我的BufferedReader代码会泄漏内存?,java,memory-leaks,gzip,bufferedreader,Java,Memory Leaks,Gzip,Bufferedreader,我有一个BufferedReader的包装器,它一个接一个地读入文件,在多个文件之间创建一个不间断的流: import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Re

我有一个
BufferedReader
的包装器,它一个接一个地读入文件,在多个文件之间创建一个不间断的流:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.zip.GZIPInputStream;

/**
 * reads in a whole bunch of files such that when one ends it moves to the
 * next file.
 * 
 * @author isaak
 *
 */
class LogFileStream implements FileStreamInterface{
    private ArrayList<String> fileNames;
    private BufferedReader br;
    private boolean done = false;

    /**
    * 
    * @param files an array list of files to read from, order matters.
    * @throws IOException
    */
    public LogFileStream(ArrayList<String> files) throws IOException {
        fileNames = new ArrayList<String>();
        for (int i = 0; i < files.size(); i++) {
            fileNames.add(files.get(i));
        }
        setFile();
    }

    /**
     * advances the file that this class is reading from.
     * 
     * @throws IOException
     */
    private void setFile() throws IOException {
        if (fileNames.size() == 0) {
            this.done = true;
            return;
        }
        if (br != null) {
            br.close();
        }
        //if the file is a .gz file do a little extra work.
        //otherwise read it in with a standard file Reader
        //in either case, set the buffer size to 128kb
        if (fileNames.get(0).endsWith(".gz")) {
            InputStream fileStream = new FileInputStream(fileNames.get(0));
            InputStream gzipStream = new GZIPInputStream(fileStream);
            // TODO this probably needs to be modified to work well on any
            // platform, UTF-8 is standard for debian/novastar though.
            Reader decoder = new InputStreamReader(gzipStream, "UTF-8");
            // note that the buffer size is set to 128kb instead of the standard
            // 8kb.
            br = new BufferedReader(decoder, 131072);
            fileNames.remove(0);
        } else {
            FileReader filereader = new FileReader(fileNames.get(0));
            br = new BufferedReader(filereader, 131072);
            fileNames.remove(0);
        }
    }

    /**
     * returns true if there are more lines available to read.
     * @return true if there are more lines available to read.
     */
    public boolean hasMore() {
        return !done;
    }

    /**
      * Gets the next line from the correct file.
      * @return the next line from the files, if there isn't one it returns null
      * @throws IOException
      */
    public String nextLine() throws IOException {
        if (done == true) {
            return null;
        }
        String line = br.readLine();
        if (line == null) {
            setFile();
            return nextLine();
        }
        return line;
    }
}
导入java.io.BufferedReader;
导入java.io.FileInputStream;
导入java.io.FileReader;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.InputStreamReader;
导入java.io.Reader;
导入java.util.ArrayList;
导入java.util.zip.gzip输入流;
/**
*读入一大堆文件,这样当一个文件结束时,它就会移动到另一个文件
*下一个文件。
* 
*@作者伊萨克
*
*/
类LogFileStream实现FileStreamInterface{
私有ArrayList文件名;
专用缓冲读取程序br;
私有布尔完成=假;
/**
* 
*@param files要读取的文件数组列表,顺序问题。
*@抛出异常
*/
公共日志文件流(ArrayList文件)引发IOException{
fileNames=newarraylist();
对于(int i=0;i
如果我在一个大的文件列表(价值300MB的文件)上构造这个对象,那么在while循环中反复打印
nextLine()
,性能会不断下降,直到没有更多的RAM可用。即使我正在读取约500kb的文件,并且使用的虚拟机内存为32MB,也会发生这种情况

我希望这段代码能够在海量数据集(价值数百GB的文件)上运行,并且它是需要使用32MB或更少内存运行的程序的一个组件

所使用的文件大多标记为CSV文件,因此使用Gzip将其压缩到磁盘上。这个阅读器需要处理gzip和未压缩的文件


如果我错了,请纠正我,但一旦文件被读取,并且其行从该文件中吐出数据,与该文件相关的对象以及所有其他内容都应该可以进行垃圾收集?

在您关闭连接/读取器后,GC开始工作。如果您使用java 7或以上,您可能需要考虑使用It RealEntRealSt声明,这是处理IO操作的一种更好的方法。

< P>最后一次调用SETFILE文件不会关闭BuffReDeLead,因此您正在泄漏重新资源。p> 实际上,在nextLine中,您将读取第一个文件,直到最后。当到达终点时,调用setFile并检查是否还有更多文件要处理。但是,如果没有更多文件,您可以立即返回,而不关闭最后一个BufferReader用户


此外,如果不处理所有文件,您将有一个ressource仍在使用。

代码中至少有一个漏洞:方法
setFile()
不会关闭最后一个
BufferedReader
,因为
if(fileNames.size()==0)
检查在
if(br!=null)
检查之前

但是,只有在多次实例化
LogFileStream
时,这才可能导致所描述的效果


最好使用
LinkedList
而不是ArrayList作为
文件名。在ArrayList上删除(0)
比在LinkedList上更“昂贵”。您可以在构造函数中使用以下单行来实例化它:
fileNames=newlinkedlist(files)

每隔一段时间,您可以
flush()
close()
使用
BufferedReader
。这将清除读卡器的内容,因此每次使用
setFile()
方法时,可能都会刷新读卡器。然后,就在每次调用之前,比如
br=new BufferedReader(decoder,131072)
close()
使用Java 8的
BufferedReader
,GZIP支持已经从Java代码转移到本机
zlib
用法

非封闭的GZIP流泄漏本机内存(我实际上说的是“本机”而不是“堆”内存),而且诊断起来很不容易。根据这些流的应用程序使用情况,操作系统可能会很快达到其内存限制

症状是操作系统进程m