# HG changeset patch # User rriggs # Date 1539701728 14400 # Node ID 0edbbc64393cc84b022afa92f983cb61f1a58313 # Parent 2d9f8845d0aef2dbd8a38c7d40707a71fe9d9d18 8192939: Remove Finalize methods from FileInputStream and FileOutputStream Reviewed-by: alanb, iris, mchung diff -r 2d9f8845d0ae -r 0edbbc64393c src/java.base/share/classes/java/io/FileInputStream.java --- a/src/java.base/share/classes/java/io/FileInputStream.java Tue Oct 16 11:08:46 2018 -0400 +++ b/src/java.base/share/classes/java/io/FileInputStream.java Tue Oct 16 10:55:28 2018 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,8 +79,6 @@ private volatile boolean closed; - private final Object altFinalizer; - /** * Creates a FileInputStream by * opening a connection to an actual file, @@ -155,10 +153,7 @@ fd.attach(this); path = name; open(name); - altFinalizer = getFinalizer(this); - if (altFinalizer == null) { - FileCleanable.register(fd); // open set the fd, register the cleanup - } + FileCleanable.register(fd); // open set the fd, register the cleanup } /** @@ -195,7 +190,6 @@ } fd = fdObj; path = null; - altFinalizer = null; /* * FileDescriptor is being shared by streams. @@ -438,85 +432,4 @@ static { initIDs(); } - - /** - * 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. - * - * @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. - * 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", forRemoval = true) - protected void finalize() throws IOException { - } - - /* - * Returns a finalizer object if the FIS needs a finalizer; otherwise null. - * If the FIS has a close method; it needs an AltFinalizer. - */ - private static Object getFinalizer(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; - } - /** - * 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; - - 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 -r 2d9f8845d0ae -r 0edbbc64393c src/java.base/share/classes/java/io/FileOutputStream.java --- a/src/java.base/share/classes/java/io/FileOutputStream.java Tue Oct 16 11:08:46 2018 -0400 +++ b/src/java.base/share/classes/java/io/FileOutputStream.java Tue Oct 16 10:55:28 2018 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,8 +95,6 @@ private volatile boolean closed; - private final Object altFinalizer; - /** * Creates a file output stream to write to the file with the * specified name. A new FileDescriptor object is @@ -235,10 +233,7 @@ this.path = name; open(name, append); - altFinalizer = getFinalizer(this); - if (altFinalizer == null) { - FileCleanable.register(fd); // open sets the fd, register the cleanup - } + FileCleanable.register(fd); // open sets the fd, register the cleanup } /** @@ -274,7 +269,6 @@ } this.fd = fdObj; this.path = null; - this.altFinalizer = null; fd.attach(this); } @@ -457,98 +451,9 @@ return fc; } - /** - * Cleans up the connection to the file, and ensures that the - * {@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. - * - * @exception IOException if an I/O error occurs. - * @see java.io.FileInputStream#close() - */ - @Deprecated(since="9", forRemoval = true) - protected void finalize() throws IOException { - } - private static native void initIDs(); static { initIDs(); } - - /* - * Returns a finalizer object if the FOS needs a finalizer; otherwise null. - * If the FOS has a close method; it needs an AltFinalizer. - */ - private static Object getFinalizer(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; - } - - /** - * 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; - - 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 -r 2d9f8845d0ae -r 0edbbc64393c test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java --- a/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java Tue Oct 16 11:08:46 2018 -0400 +++ b/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java Tue Oct 16 10:55:28 2018 -0400 @@ -62,15 +62,11 @@ */ public class UnreferencedFISClosesFd { - enum CleanupType { - CLOSE, // Cleanup is handled via calling close - CLEANER} // Cleanup is handled via Cleaner - static final String FILE_NAME = "empty.txt"; /** * Subclass w/ no overrides; not finalize or close. - * Cleanup should be via the Cleaner (not close). + * Cleanup should be via the Cleaner. */ public static class StreamOverrides extends FileInputStream { @@ -88,7 +84,7 @@ /** * Subclass overrides close. - * Cleanup should be via AltFinalizer calling close(). + * Cleanup should be via the Cleaner. */ public static class StreamOverridesClose extends StreamOverrides { @@ -104,7 +100,7 @@ /** * Subclass overrides finalize. - * Cleanup should be via the Cleaner (not close). + * Cleanup should be via the Cleaner. */ public static class StreamOverridesFinalize extends StreamOverrides { @@ -113,7 +109,7 @@ } @SuppressWarnings({"deprecation","removal"}) - protected void finalize() throws IOException { + protected void finalize() throws IOException, Throwable { super.finalize(); } } @@ -129,7 +125,7 @@ } @SuppressWarnings({"deprecation","removal"}) - protected void finalize() throws IOException { + protected void finalize() throws IOException, Throwable { super.finalize(); } } @@ -149,15 +145,15 @@ long fdCount0 = getFdCount(); int failCount = 0; - failCount += test(new FileInputStream(name), CleanupType.CLEANER); + failCount += test(new FileInputStream(name)); - failCount += test(new StreamOverrides(name), CleanupType.CLEANER); + failCount += test(new StreamOverrides(name)); - failCount += test(new StreamOverridesClose(name), CleanupType.CLOSE); + failCount += test(new StreamOverridesClose(name)); - failCount += test(new StreamOverridesFinalize(name), CleanupType.CLEANER); + failCount += test(new StreamOverridesFinalize(name)); - failCount += test(new StreamOverridesFinalizeClose(name), CleanupType.CLOSE); + failCount += test(new StreamOverridesFinalizeClose(name)); if (failCount > 0) { throw new AssertionError("Failed test count: " + failCount); @@ -180,7 +176,7 @@ : -1L; } - private static int test(FileInputStream fis, CleanupType cleanType) throws Exception { + private static int test(FileInputStream fis) throws Exception { try { System.out.printf("%nTesting %s%n", fis.getClass().getName()); @@ -199,35 +195,18 @@ fdField.setAccessible(true); int ffd = fdField.getInt(fd); - Field altFinalizerField = FileInputStream.class.getDeclaredField("altFinalizer"); - altFinalizerField.setAccessible(true); - Object altFinalizer = altFinalizerField.get(fis); - if ((altFinalizer != null) ^ (cleanType == CleanupType.CLOSE)) { - throw new RuntimeException("Unexpected AltFinalizer: " + altFinalizer - + ", for " + cleanType); - } - Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup"); cleanupField.setAccessible(true); Object cleanup = cleanupField.get(fd); - System.out.printf(" cleanup: %s, alt: %s, ffd: %d, cf: %s%n", - cleanup, altFinalizer, ffd, cleanupField); - if ((cleanup != null) ^ (cleanType == CleanupType.CLEANER)) { - throw new Exception("unexpected cleanup: " - + cleanup + ", for " + cleanType); + System.out.printf(" cleanup: %s, ffd: %d, cf: %s%n", cleanup, ffd, cleanupField); + if (cleanup == null) { + throw new RuntimeException("cleanup should not be null"); } - if (cleanup != null) { - WeakReference cleanupWeak = new WeakReference<>(cleanup, queue); - pending.add(cleanupWeak); - System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n", - fdWeak, msWeak, cleanupWeak); - } - if (altFinalizer != null) { - WeakReference altFinalizerWeak = new WeakReference<>(altFinalizer, queue); - pending.add(altFinalizerWeak); - System.out.printf(" fdWeak: %s%n msWeak: %s%n altFinalizerWeak: %s%n", - fdWeak, msWeak, altFinalizerWeak); - } + + WeakReference cleanupWeak = new WeakReference<>(cleanup, queue); + pending.add(cleanupWeak); + System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n", + fdWeak, msWeak, cleanupWeak); AtomicInteger closeCounter = fis instanceof StreamOverrides ? ((StreamOverrides)fis).closeCounter() : null; @@ -243,28 +222,16 @@ fis = null; fd = null; cleanup = null; - altFinalizer = null; System.gc(); // attempt to reclaim them } } Reference.reachabilityFence(fd); Reference.reachabilityFence(fis); Reference.reachabilityFence(cleanup); - Reference.reachabilityFence(altFinalizer); // Confirm the correct number of calls to close depending on the cleanup type - switch (cleanType) { - case CLEANER: - if (closeCounter != null && closeCounter.get() > 0) { - throw new RuntimeException("Close should not have been called: count: " - + closeCounter); - } - break; - case CLOSE: - if (closeCounter == null || closeCounter.get() == 0) { - throw new RuntimeException("Close should have been called: count: 0"); - } - break; + if (closeCounter != null && closeCounter.get() > 0) { + throw new RuntimeException("Close should not have been called: count: " + closeCounter); } } catch (Exception ex) { ex.printStackTrace(System.out); diff -r 2d9f8845d0ae -r 0edbbc64393c test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java --- a/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java Tue Oct 16 11:08:46 2018 -0400 +++ b/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java Tue Oct 16 10:55:28 2018 -0400 @@ -28,8 +28,7 @@ * @library /test/lib * @build jdk.test.lib.util.FileUtils UnreferencedFOSClosesFd * @bug 6524062 - * @summary Test to ensure that FOS.finalize() invokes the close() method as per - * the specification. + * @summary Test to ensure that the fd is closed if left unreferenced * @run main/othervm UnreferencedFOSClosesFd */ import java.io.File; @@ -54,15 +53,11 @@ public class UnreferencedFOSClosesFd { - enum CleanupType { - CLOSE, // Cleanup is handled via calling close - CLEANER} // Cleanup is handled via Cleaner - static final String FILE_NAME = "empty.txt"; /** - * Subclass w/ no overrides; not finalize or close. - * Cleanup should be via the Cleaner (not close). + * Subclass w/ no overrides; not close. + * Cleanup should be via the Cleaner. */ public static class StreamOverrides extends FileOutputStream { @@ -96,7 +91,7 @@ /** * Subclass overrides finalize and close. - * Cleanup should be via the Cleaner (not close). + * Cleanup should be via the Cleaner. */ public static class StreamOverridesFinalize extends StreamOverrides { @@ -105,14 +100,14 @@ } @SuppressWarnings({"deprecation","removal"}) - protected void finalize() throws IOException { + protected void finalize() throws IOException, Throwable { super.finalize(); } } /** * Subclass overrides finalize and close. - * Cleanup should be via AltFinalizer calling close(). + * Cleanup should be via the Cleaner. */ public static class StreamOverridesFinalizeClose extends StreamOverridesClose { @@ -121,7 +116,7 @@ } @SuppressWarnings({"deprecation","removal"}) - protected void finalize() throws IOException { + protected void finalize() throws IOException, Throwable { super.finalize(); } } @@ -131,8 +126,6 @@ */ public static void main(String argv[]) throws Exception { - - File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME); inFile.createNewFile(); inFile.deleteOnExit(); @@ -143,15 +136,15 @@ long fdCount0 = getFdCount(); int failCount = 0; - failCount += test(new FileOutputStream(name), CleanupType.CLEANER); + failCount += test(new FileOutputStream(name)); - failCount += test(new StreamOverrides(name), CleanupType.CLEANER); + failCount += test(new StreamOverrides(name)); - failCount += test(new StreamOverridesClose(name), CleanupType.CLOSE); + failCount += test(new StreamOverridesClose(name)); - failCount += test(new StreamOverridesFinalize(name), CleanupType.CLEANER); + failCount += test(new StreamOverridesFinalize(name)); - failCount += test(new StreamOverridesFinalizeClose(name), CleanupType.CLOSE); + failCount += test(new StreamOverridesFinalizeClose(name)); if (failCount > 0) { throw new AssertionError("Failed test count: " + failCount); @@ -174,7 +167,7 @@ : -1L; } - private static int test(FileOutputStream fos, CleanupType cleanType) throws Exception { + private static int test(FileOutputStream fos) throws Exception { try { System.out.printf("%nTesting %s%n", fos.getClass().getName()); @@ -193,35 +186,18 @@ fdField.setAccessible(true); int ffd = fdField.getInt(fd); - Field altFinalizerField = FileOutputStream.class.getDeclaredField("altFinalizer"); - altFinalizerField.setAccessible(true); - Object altFinalizer = altFinalizerField.get(fos); - if ((altFinalizer != null) ^ (cleanType == CleanupType.CLOSE)) { - throw new RuntimeException("Unexpected AltFinalizer: " + altFinalizer - + ", for " + cleanType); - } - Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup"); cleanupField.setAccessible(true); Object cleanup = cleanupField.get(fd); - System.out.printf(" cleanup: %s, alt: %s, ffd: %d, cf: %s%n", - cleanup, altFinalizer, ffd, cleanupField); - if ((cleanup != null) ^ (cleanType == CleanupType.CLEANER)) { - throw new Exception("unexpected cleanup: " - + cleanup + ", for " + cleanType); + System.out.printf(" cleanup: %s, ffd: %d, cf: %s%n", cleanup, ffd, cleanupField); + if (cleanup == null) { + throw new RuntimeException("cleanup should not be null"); } - if (cleanup != null) { - WeakReference cleanupWeak = new WeakReference<>(cleanup, queue); - pending.add(cleanupWeak); - System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n", - fdWeak, msWeak, cleanupWeak); - } - if (altFinalizer != null) { - WeakReference altFinalizerWeak = new WeakReference<>(altFinalizer, queue); - pending.add(altFinalizerWeak); - System.out.printf(" fdWeak: %s%n msWeak: %s%n altFinalizerWeak: %s%n", - fdWeak, msWeak, altFinalizerWeak); - } + + WeakReference cleanupWeak = new WeakReference<>(cleanup, queue); + pending.add(cleanupWeak); + System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n", + fdWeak, msWeak, cleanupWeak); AtomicInteger closeCounter = fos instanceof StreamOverrides ? ((StreamOverrides) fos).closeCounter() : null; @@ -237,28 +213,16 @@ fos = null; fd = null; cleanup = null; - altFinalizer = null; System.gc(); // attempt to reclaim them } } Reference.reachabilityFence(fd); Reference.reachabilityFence(fos); Reference.reachabilityFence(cleanup); - Reference.reachabilityFence(altFinalizer); // Confirm the correct number of calls to close depending on the cleanup type - switch (cleanType) { - case CLEANER: - if (closeCounter != null && closeCounter.get() > 0) { - throw new RuntimeException("Close should not have been called: count: " - + closeCounter); - } - break; - case CLOSE: - if (closeCounter == null || closeCounter.get() == 0) { - throw new RuntimeException("Close should have been called: count: 0"); - } - break; + if (closeCounter != null && closeCounter.get() > 0) { + throw new RuntimeException("Close should not have been called: count: " + closeCounter); } } catch (Exception ex) { ex.printStackTrace(System.out);