为什么 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
的构造函数,但它也允许您设置缓冲区大小。
无论如何,我猜它的设计方式有一些合乎逻辑的原因,而我只是没能理解它。希望有人能赐教...
这也适用于 DeflaterInputStream
、DeflaterOutputStream
和 InflaterOutputStream
等。
与许多 "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
是一种资源,因此应用正常的资源管理规则。
在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
的构造函数,但它也允许您设置缓冲区大小。
无论如何,我猜它的设计方式有一些合乎逻辑的原因,而我只是没能理解它。希望有人能赐教...
这也适用于 DeflaterInputStream
、DeflaterOutputStream
和 InflaterOutputStream
等。
与许多 "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
是一种资源,因此应用正常的资源管理规则。