In Java 8 the close()
method for InflaterInputStream
is as shown below
public void close() throws IOException {
if (!closed) {
if (usesDefaultInflater)
inf.end();
in.close();
closed = true;
}
}
usesDefaultInflater
is a boolean that is only true
if the constructor below is used
public InflaterInputStream(InputStream in) {
this(in, new Inflater());
usesDefaultInflater = true;
}
Any other constructor such as this one below results in this boolean being set to false
new InflaterInputStream(decryptInputStream, new Inflater(), 4096);
As a result, unless you use the default constructor the end()
method is not called on the Inflater
and this means unnecessary native memory consumption until the finalize
method is called on the Inflater
by the Finalizer thread potentially a long time after the InflaterInputStream
is closed. See the implementation in Inflater
below.
/**
* 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();
}
To get around this you need to override the close
method on the InflaterInputStream
like so
new InflaterInputStream(decryptInputStream, new Inflater(), 4096) {
@Override
public void close() throws IOException {
try {
super.close();
} finally {
inf.end();
}
}
}
This is easily missed and it seems to me like it might have been sensible to call end()
by default and allow the user to override that behaviour by providing a constructor where you could specify false
, or at the very least a constructor that uses the default Inflater
but that also allows you to set the buffer size.
Anyway, I'm guessing there's some logical reason that it's designed the way it is and I've just failed to grok it. Hoping someone can enlighten me...
This also applies to DeflaterInputStream
, DeflaterOutputStream
, and InflaterOutputStream
amongst others.
There are many methods in the Java Runtime Library that takes e.g. an OutputStream
(such as Files.copy()
). Unless those methods explicitly state that the stream will be closed by the method, the stream will not be closed. Closing the stream is the responsibility of the stream "owner", e.g. the caller of the method.
Similarly, neither constructor of InflaterInputStream
that takes an Inflater
states that they will end()
the Inflater
, which means that they won't. It is up to the caller to end it, when needed.
When using the constructor that creates the Inflater
for you, the InflaterInputStream
becomes the "owner" of that internal Inflater
, and it is therefore the responsibility of the InflaterInputStream
to end the Inflater
.
The general guideline for resource management is that, unless otherwise documented, whoever allocates a resource is responsible for releasing (closing, ending, ...) the resource.
Inflater
is a resource, so normal resource management rules apply.
As with many "why" questions, this is an educated guess. I didn't see any explicit explanation for this, so who knows what the original programmer was thinking? Anyway, take my answer with a grain of salt.
The other constructors all take an Inflater
instance, which means that the user has a reference to the (internal) Inflater
. Note that these classes have no getter to get the Inflater
out. So the only way the user would have a reference to it, is by passing it from outside (well, that and using reflection, but let's not go there).
So perhaps the assumption was that since the user passed his own Inflater
instance, then he wants to manage the Inflater
himself, possibly reusing it after this steam has ended. Thus, closing the Inflater
when the stream is closed isn't a good idea.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With