8080225: FileInput/OutputStream/FileChannel cleanup should be improved

Reviewed-by: mchung, plevart, bpb
This commit is contained in:
Roger Riggs 2017-12-01 16:40:08 -05:00
parent b93586c51e
commit f29e21abb1
23 changed files with 1104 additions and 261 deletions

View file

@ -25,6 +25,7 @@
package java.io;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import sun.nio.ch.FileChannelImpl;
@ -37,6 +38,22 @@ import sun.nio.ch.FileChannelImpl;
* <p><code>FileInputStream</code> is meant for reading streams of raw bytes
* such as image data. For reading streams of characters, consider using
* <code>FileReader</code>.
*
* @apiNote
* To release resources used by this stream {@link #close} should be called
* directly or by try-with-resources. Subclasses are responsible for the cleanup
* of resources acquired by the subclass.
* Subclasses that override {@link #finalize} in order to perform cleanup
* should be modified to use alternative cleanup mechanisms such as
* {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
*
* @implSpec
* If this FileInputStream has been subclassed and the {@link #close}
* method has been overridden, the {@link #close} method will be
* called when the FileInputStream is unreachable.
* Otherwise, it is implementation specific how the resource cleanup described in
* {@link #close} is performed.
*
* @author Arthur van Hoff
* @see java.io.File
@ -63,6 +80,8 @@ class FileInputStream extends InputStream
private volatile boolean closed;
private final AltFinalizer altFinalizer;
/**
* Creates a <code>FileInputStream</code> by
* opening a connection to an actual file,
@ -137,6 +156,10 @@ class FileInputStream extends InputStream
fd.attach(this);
path = name;
open(name);
altFinalizer = AltFinalizer.get(this);
if (altFinalizer == null) {
fd.registerCleanup(); // open set the fd, register the cleanup
}
}
/**
@ -173,6 +196,7 @@ class FileInputStream extends InputStream
}
fd = fdObj;
path = null;
altFinalizer = null;
/*
* FileDescriptor is being shared by streams.
@ -316,6 +340,14 @@ class FileInputStream extends InputStream
* <p> If this stream has an associated channel then the channel is closed
* as well.
*
* @apiNote
* Overriding {@link #close} to perform cleanup actions is reliable
* only when called directly or when called by try-with-resources.
* Do not depend on finalization to invoke {@code close};
* finalization is not reliable and is deprecated.
* If cleanup of native resources is needed, other mechanisms such as
* {@linkplain java.lang.ref.Cleaner} should be used.
*
* @exception IOException if an I/O error occurs.
*
* @revised 1.4
@ -404,16 +436,27 @@ class FileInputStream extends InputStream
private static native void initIDs();
static {
initIDs();
}
/**
* Ensures that the <code>close</code> method of this file input stream is
* Ensures that the {@link #close} method of this file input stream is
* called when there are no more references to it.
* The {@link #finalize} method does not call {@link #close} directly.
*
* @deprecated The {@code finalize} method has been deprecated.
* @apiNote
* To release resources used by this stream {@link #close} should be called
* directly or by try-with-resources.
*
* @implSpec
* If this FileInputStream has been subclassed and the {@link #close}
* method has been overridden, the {@link #close} method will be
* called when the FileInputStream is unreachable.
* Otherwise, it is implementation specific how the resource cleanup described in
* {@link #close} is performed.
*
* @deprecated The {@code finalize} method has been deprecated and will be removed.
* Subclasses that override {@code finalize} in order to perform cleanup
* should be modified to use alternative cleanup mechanisms and
* to remove the overriding {@code finalize} method.
@ -425,15 +468,57 @@ class FileInputStream extends InputStream
* @exception IOException if an I/O error occurs.
* @see java.io.FileInputStream#close()
*/
@Deprecated(since="9")
@Deprecated(since="9", forRemoval = true)
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
/**
* Class to call {@code FileInputStream.close} when finalized.
* If finalization of the stream is needed, an instance is created
* in its constructor(s). When the set of instances
* related to the stream is unreachable, the AltFinalizer performs
* the needed call to the stream's {@code close} method.
*/
static class AltFinalizer {
private final FileInputStream fis;
/*
* Returns a finalizer object if the FIS needs a finalizer; otherwise null.
* If the FIS has a close method; it needs an AltFinalizer.
*/
static AltFinalizer get(FileInputStream fis) {
Class<?> clazz = fis.getClass();
while (clazz != FileInputStream.class) {
try {
clazz.getDeclaredMethod("close");
return new AltFinalizer(fis);
} catch (NoSuchMethodException nsme) {
// ignore
}
clazz = clazz.getSuperclass();
}
return null;
}
private AltFinalizer(FileInputStream fis) {
this.fis = fis;
}
@Override
@SuppressWarnings("deprecation")
protected final void finalize() {
try {
if ((fis.fd != null) && (fis.fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
fis.close();
}
} catch (IOException ioe) {
// ignore
}
}
}
}