finalized

慎用FileBackedOutputStream

在项目中遇到一个FileBackedOutputStream的坑,在此记录一下。

因为某些原因,需要实现一个ResettableInputStream,大家都知道,JDK中,ByteArrayInputStream是可以mark、reset的,但是代价就是将所有数据都load进内存,在数据量较大时,这是无法接受的。因为我们需要的是任意的在流中穿梭的能力,这时候,可以借助磁盘来完成这一功能。索性,Guava中提供了一个类,FileBackedOutputStream,将数据写入其中之后,如果数据超过一定限度,它就会将数据写入磁盘。

FileBackedOutputStream的构造函数如下所示,里面提供了一个叫supplier的类,这个类作为OutputStream的出口。这里应该注意一下,如果resetOnFinalize这一个参数如果设为true的话,supplier会重写finalize方法,里面调用reset操作。这也是本篇文章出现的原因。

  public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) {
    this.fileThreshold = fileThreshold;
    this.resetOnFinalize = resetOnFinalize;
    memory = new MemoryOutput();
    out = memory;

    if (resetOnFinalize) {
      supplier = new InputSupplier<InputStream>() {
        @Override
        public InputStream getInput() throws IOException {
          return openStream();
        }

        @Override protected void finalize() {
          try {
            reset();
          } catch (Throwable t) {
            t.printStackTrace(System.err);
          }
        }
      };
    } else {
      supplier = new InputSupplier<InputStream>() {
        @Override
        public InputStream getInput() throws IOException {
          return openStream();
        }
      };
    }
  }

  public InputSupplier<InputStream> getSupplier() {
    return supplier;
  }