为什么 Java 的 InflaterInputStream(以及其他类似的 类)仅在其内部 Inflater 上有条件地调用结束

Why does Java's InflaterInputStream (and other similar classes) only conditionally call end on it's internal Inflater

在Java8中close()方法InflaterInputStream如下所示

public void close() throws IOException {
    if (!closed) {
        if (usesDefaultInflater)
            inf.end();
        in.close();
        closed = true;
    }
}

usesDefaultInflater 是一个布尔值,只有 true 如果使用下面的构造函数

public InflaterInputStream(InputStream in) {
    this(in, new Inflater());
    usesDefaultInflater = true;
}

任何其他构造函数(例如下面的构造函数)都会导致此布尔值设置为 false

new InflaterInputStream(decryptInputStream, new Inflater(), 4096);

因此,除非您使用默认构造函数,否则不会在 Inflater 上调用 end() 方法,这意味着在调用 finalize 方法之前会消耗不必要的本机内存Inflater 可能在 InflaterInputStream 关闭后很长时间由终结器线程执行。请参阅下面 Inflater 中的实现。

/**
 * Closes the decompressor and discards any unprocessed input.
 * This method should be called when the decompressor is no longer
 * being used, but will also be called automatically by the finalize()
 * method. Once this method is called, the behavior of the Inflater
 * object is undefined.
 */
public void end() {
    synchronized (zsRef) {
        long addr = zsRef.address();
        zsRef.clear();
        if (addr != 0) {
            end(addr);
            buf = null;
        }
    }
}

/**
 * Closes the decompressor when garbage is collected.
 */
protected void finalize() {
    end();
}

要解决这个问题,您需要像这样

重写 InflaterInputStream 上的 close 方法
new InflaterInputStream(decryptInputStream, new Inflater(), 4096) {    
    @Override
    public void close() throws IOException {
        try {
            super.close();
        } finally {
            inf.end();
        }
    }
}

这很容易被忽略,在我看来,默认调用 end() 并允许用户通过提供可以指定 false 的构造函数来覆盖该行为可能是明智的,或者至少是一个使用默认 Inflater 的构造函数,但它也允许您设置缓冲区大小。

无论如何,我猜它的设计方式有一些合乎逻辑的原因,而我只是没能理解它。希望有人能赐教...

这也适用于 DeflaterInputStreamDeflaterOutputStreamInflaterOutputStream 等。

与许多 "why" 问题一样,这是有根据的猜测。我没有看到任何明确的解释,所以谁知道原来的程序员在想什么?无论如何,请对我的回答持保留态度。

其他构造函数都采用 Inflater 实例,这意味着用户具有对(内部)Inflater 的引用。请注意,这些 类 没有 getter 来获取 Inflater。因此,用户引用它的唯一方法是从外部传递它(好吧,那并使用反射,但我们不要去那里)。

所以假设可能是因为用户传递了他自己的 Inflater 实例,然后他想自己管理 Inflater,可能会在这个 steam 结束后重新使用它。因此,在流关闭时关闭 Inflater 不是一个好主意。

Java 运行时库中有许多方法,例如一个 OutputStream (such as Files.copy())。除非那些方法 明确地 声明流将被该方法关闭,否则流不会被关闭。关闭流是流的责任 "owner",例如方法的调用者。

同样,InflaterInputStream that takes an Inflater states that they will end()Inflater 的构造函数都不是,这意味着它们不会。由调用者在需要时结束它。

当使用为您创建 Inflater 的构造函数时,InflaterInputStream 变成 internal Inflater 的 "owner" ],因此 InflaterInputStream 有责任结束 Inflater.

资源管理

资源管理的一般准则是,除非另有说明,否则分配资源的人负责释放(关闭、结束...)资源。

Inflater 是一种资源,因此应用正常的资源管理规则。