FileInputStream is meant for reading streams of raw bytes
* such as image data. For reading streams of characters, consider using
* FileReader.
+ *
+ * @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 FileInputStream 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
*
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 close 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
+ }
}
}
}
diff --git a/src/java.base/share/classes/java/io/FileOutputStream.java b/src/java.base/share/classes/java/io/FileOutputStream.java
index f9c1baa3518..cb9c8cda0f3 100644
--- a/src/java.base/share/classes/java/io/FileOutputStream.java
+++ b/src/java.base/share/classes/java/io/FileOutputStream.java
@@ -25,6 +25,7 @@
package java.io;
+import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.JavaIOFileDescriptorAccess;
@@ -44,6 +45,21 @@ import sun.nio.ch.FileChannelImpl;
* such as image data. For writing streams of characters, consider using
* FileWriter.
*
+ * @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 FileOutputStream 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
* @see java.io.FileDescriptor
@@ -80,6 +96,8 @@ class FileOutputStream extends OutputStream
private volatile boolean closed;
+ private final AltFinalizer altFinalizer;
+
/**
* Creates a file output stream to write to the file with the
* specified name. A new FileDescriptor object is
@@ -218,6 +236,10 @@ class FileOutputStream extends OutputStream
this.path = name;
open(name, append);
+ altFinalizer = AltFinalizer.get(this);
+ if (altFinalizer == null) {
+ fd.registerCleanup(); // open set the fd, register the cleanup
+ }
}
/**
@@ -253,6 +275,7 @@ class FileOutputStream extends OutputStream
}
this.fd = fdObj;
this.path = null;
+ this.altFinalizer = null;
fd.attach(this);
}
@@ -340,6 +363,14 @@ class FileOutputStream extends OutputStream
*
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
@@ -429,34 +460,35 @@ class FileOutputStream extends OutputStream
/**
* Cleans up the connection to the file, and ensures that the
- * close method of this file output stream is
+ * {@link #close} method of this file output stream is
* called when there are no more references to this stream.
+ * The {@link #finalize} method does not call {@link #close} directly.
+ *
+ * @apiNote
+ * To release resources used by this stream {@link #close} should be called
+ * directly or by try-with-resources.
+ *
+ * @implSpec
+ * If this FileOutputStream has been subclassed and the {@link #close}
+ * method has been overridden, the {@link #close} method will be
+ * called when the FileOutputStream 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.
+ * When overriding the {@code finalize} method, its implementation must explicitly
+ * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
+ * See the specification for {@link Object#finalize()} for further
+ * information about migration options.
*
- * @deprecated The {@code finalize} method has been deprecated.
- * 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.
- * When overriding the {@code finalize} method, its implementation must explicitly
- * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
- * See the specification for {@link Object#finalize()} for further
- * information about migration options.
* @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) {
- if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
- flush();
- } else {
- /* 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();
- }
- }
}
private static native void initIDs();
@@ -465,4 +497,59 @@ class FileOutputStream extends OutputStream
initIDs();
}
+ /**
+ * Class to call {@code FileOutputStream.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 FileOutputStream fos;
+
+ /*
+ * Returns a finalizer object if the FOS needs a finalizer; otherwise null.
+ * If the FOS has a close method; it needs an AltFinalizer.
+ */
+ static AltFinalizer get(FileOutputStream fos) {
+ Class> clazz = fos.getClass();
+ while (clazz != FileOutputStream.class) {
+ try {
+ clazz.getDeclaredMethod("close");
+ return new AltFinalizer(fos);
+ } catch (NoSuchMethodException nsme) {
+ // ignore
+ }
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+
+ private AltFinalizer(FileOutputStream fos) {
+ this.fos = fos;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected final void finalize() {
+ try {
+ if (fos.fd != null) {
+ if (fos.fd == FileDescriptor.out || fos.fd == FileDescriptor.err) {
+ // Subclass may override flush; otherwise it is no-op
+ fos.flush();
+ } else {
+ /* 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()
+ */
+ fos.close();
+ }
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ }
+
}
diff --git a/src/java.base/share/classes/java/io/RandomAccessFile.java b/src/java.base/share/classes/java/io/RandomAccessFile.java
index 5c946d1d715..4b6e66ff140 100644
--- a/src/java.base/share/classes/java/io/RandomAccessFile.java
+++ b/src/java.base/share/classes/java/io/RandomAccessFile.java
@@ -257,6 +257,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
fd.attach(this);
path = name;
open(name, imode);
+ fd.registerCleanup(); // open sets the fd, register the cleanup
}
/**
diff --git a/src/java.base/share/classes/java/net/SocketInputStream.java b/src/java.base/share/classes/java/net/SocketInputStream.java
index 04ee3378eaa..bb3481b7164 100644
--- a/src/java.base/share/classes/java/net/SocketInputStream.java
+++ b/src/java.base/share/classes/java/net/SocketInputStream.java
@@ -283,7 +283,7 @@ class SocketInputStream extends FileInputStream
/**
* Overrides finalize, the fd is closed by the Socket.
*/
- @SuppressWarnings("deprecation")
+ @SuppressWarnings({"deprecation", "removal"})
protected void finalize() {}
/**
diff --git a/src/java.base/share/classes/java/net/SocketOutputStream.java b/src/java.base/share/classes/java/net/SocketOutputStream.java
index 0ba877bf56e..ddf3dcf9bb5 100644
--- a/src/java.base/share/classes/java/net/SocketOutputStream.java
+++ b/src/java.base/share/classes/java/net/SocketOutputStream.java
@@ -175,7 +175,7 @@ class SocketOutputStream extends FileOutputStream
/**
* Overrides finalize, the fd is closed by the Socket.
*/
- @SuppressWarnings("deprecation")
+ @SuppressWarnings({"deprecation", "removal"})
protected void finalize() {}
/**
diff --git a/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java b/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java
index 017aefb16af..a82d04d6eed 100644
--- a/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java
@@ -25,6 +25,7 @@
package jdk.internal.misc;
import java.io.FileDescriptor;
+import java.io.IOException;
/*
* @author Chris Hegarty
@@ -35,7 +36,8 @@ public interface JavaIOFileDescriptorAccess {
public int get(FileDescriptor fdo);
public void setAppend(FileDescriptor fdo, boolean append);
public boolean getAppend(FileDescriptor fdo);
- public void close(FileDescriptor fdo);
+ public void close(FileDescriptor fdo) throws IOException;
+ public void registerCleanup(FileDescriptor fdo);
// Only valid on Windows
public void setHandle(FileDescriptor fdo, long handle);
diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
index 5e48758bac0..274a17851e2 100644
--- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
@@ -27,6 +27,7 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.lang.ref.Cleaner.Cleanable;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
@@ -109,7 +110,12 @@ public class FileChannelImpl
}
public void run() {
- fdAccess.close(fd);
+ try {
+ fdAccess.close(fd);
+ } catch (IOException ioe) {
+ // Rethrow as unchecked so the exception can be propagated as needed
+ throw new UncheckedIOException("close", ioe);
+ }
}
}
@@ -188,7 +194,11 @@ public class FileChannelImpl
} else if (closer != null) {
// Perform the cleaning action so it is not redone when
// this channel becomes phantom reachable.
- closer.clean();
+ try {
+ closer.clean();
+ } catch (UncheckedIOException uioe) {
+ throw uioe.getCause();
+ }
} else {
fdAccess.close(fd);
}
diff --git a/src/java.base/unix/classes/java/io/FileDescriptor.java b/src/java.base/unix/classes/java/io/FileDescriptor.java
index 6c20e9d5763..5e0b80eb625 100644
--- a/src/java.base/unix/classes/java/io/FileDescriptor.java
+++ b/src/java.base/unix/classes/java/io/FileDescriptor.java
@@ -25,10 +25,14 @@
package java.io;
+import java.lang.ref.Cleaner;
import java.util.ArrayList;
import java.util.List;
+
import jdk.internal.misc.JavaIOFileDescriptorAccess;
import jdk.internal.misc.SharedSecrets;
+import jdk.internal.ref.CleanerFactory;
+import jdk.internal.ref.PhantomCleanable;
/**
* Instances of the file descriptor class serve as an opaque handle
@@ -64,7 +68,7 @@ public final class FileDescriptor {
SharedSecrets.setJavaIOFileDescriptorAccess(
new JavaIOFileDescriptorAccess() {
public void set(FileDescriptor fdo, int fd) {
- fdo.fd = fd;
+ fdo.set(fd);
}
public int get(FileDescriptor fdo) {
@@ -79,10 +83,14 @@ public final class FileDescriptor {
return fdo.append;
}
- public void close(FileDescriptor fdo) {
+ public void close(FileDescriptor fdo) throws IOException {
fdo.close();
}
+ public void registerCleanup(FileDescriptor fdo) {
+ fdo.registerCleanup();
+ }
+
public void setHandle(FileDescriptor fdo, long handle) {
throw new UnsupportedOperationException();
}
@@ -94,6 +102,11 @@ public final class FileDescriptor {
);
}
+ /**
+ * Cleanup in case FileDescriptor is not explicitly closed.
+ */
+ private FDCleanup cleanup;
+
/**
* Constructs an (invalid) FileDescriptor
* object.
@@ -177,6 +190,34 @@ public final class FileDescriptor {
/* This routine initializes JNI field offsets for the class */
private static native void initIDs();
+ /**
+ * Set the fd.
+ * If setting to -1, clear the cleaner.
+ * The {@link #registerCleanup()} method should be called for new fds.
+ * @param fd the fd or -1 to indicate closed
+ */
+ @SuppressWarnings("unchecked")
+ synchronized void set(int fd) {
+ if (fd == -1 && cleanup != null) {
+ cleanup.clear();
+ cleanup = null;
+ }
+ this.fd = fd;
+ }
+
+ /**
+ * Register a cleanup for the current raw fd.
+ * Used directly in java.io and indirectly via fdAccess.
+ * The cleanup should be registered after the fd is set in the FileDescriptor.
+ */
+ @SuppressWarnings("unchecked")
+ synchronized void registerCleanup() {
+ if (cleanup != null) {
+ cleanup.clear();
+ }
+ cleanup = FDCleanup.create(this);
+ }
+
/**
* Returns true, if the file was opened for appending.
*/
@@ -185,9 +226,30 @@ public final class FileDescriptor {
/**
* Close the raw file descriptor or handle, if it has not already been closed
* and set the fd and handle to -1.
+ * Clear the cleaner so the close does not happen twice.
* Package private to allow it to be used in java.io.
+ * @throws IOException if close fails
*/
- native void close();
+ @SuppressWarnings("unchecked")
+ synchronized void close() throws IOException {
+ if (cleanup != null) {
+ cleanup.clear();
+ cleanup = null;
+ }
+ close0();
+ }
+
+ /*
+ * Close the raw file descriptor or handle, if it has not already been closed
+ * and set the fd and handle to -1.
+ */
+ private native void close0() throws IOException;
+
+ /*
+ * Raw close of the file descriptor.
+ * Used only for last chance cleanup.
+ */
+ private static native void cleanupClose0(int fd) throws IOException;
/*
* Package private methods to track referents.
@@ -252,4 +314,45 @@ public final class FileDescriptor {
}
}
}
+
+ /**
+ * Cleanup for a FileDescriptor when it becomes phantom reachable.
+ * Create a cleanup if fd != -1.
+ * Subclassed from {@code PhantomCleanable} so that {@code clear} can be
+ * called to disable the cleanup when the fd is closed by any means other
+ * than calling {@link FileDescriptor#close}.
+ * Otherwise, it may close the native fd after it has been reused.
+ */
+ static final class FDCleanup extends PhantomCleanable