mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8080225: FileInput/OutputStream/FileChannel cleanup should be improved
Reviewed-by: mchung, plevart, bpb
This commit is contained in:
parent
b93586c51e
commit
f29e21abb1
23 changed files with 1104 additions and 261 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
* <code>FileWriter</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 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 <code>FileDescriptor</code> 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
|
|||
* <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
|
||||
|
@ -429,34 +460,35 @@ class FileOutputStream extends OutputStream
|
|||
|
||||
/**
|
||||
* Cleans up the connection to the file, and ensures that the
|
||||
* <code>close</code> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue