8212129: Remove finalize methods from java.util.zip.ZipFIle/Inflator/Deflator
Reviewed-by: rriggs, sherman, alanb, clanger
--- a/src/java.base/share/classes/java/util/zip/Deflater.java Fri Oct 26 10:47:05 2018 -0500
+++ b/src/java.base/share/classes/java/util/zip/Deflater.java Fri Oct 26 14:02:31 2018 -0400
@@ -89,13 +89,6 @@
* 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 {@code Deflater} has been subclassed and the {@code end} method has been
- * overridden, the {@code end} method will be called by the finalization when the
- * deflater is unreachable. But the subclasses should not depend on this specific
- * implementation; the finalization is not reliable and the {@code finalize} method
- * is deprecated to be removed.
- *
* @see Inflater
* @author David Connelly
* @since 1.1
@@ -204,8 +197,8 @@
public Deflater(int level, boolean nowrap) {
this.level = level;
this.strategy = DEFAULT_STRATEGY;
- this.zsRef = DeflaterZStreamRef.get(this,
- init(level, DEFAULT_STRATEGY, nowrap));
+ this.zsRef = new DeflaterZStreamRef(this,
+ init(level, DEFAULT_STRATEGY, nowrap));
}
/**
@@ -901,21 +894,6 @@
}
}
- /**
- * Closes the compressor when garbage is collected.
- *
- * @deprecated The {@code finalize} method has been deprecated and will be
- * removed. It is implemented as a no-op. 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. The recommended cleanup for compressor is to explicitly call
- * {@code end} method when it is no longer in use. If the {@code end} is
- * not invoked explicitly the resource of the compressor will be released
- * when the instance becomes unreachable.
- */
- @Deprecated(since="9", forRemoval=true)
- protected void finalize() {}
-
private void ensureOpen() {
assert Thread.holdsLock(zsRef);
if (zsRef.address() == 0)
@@ -977,43 +955,5 @@
}
}
- /*
- * If {@code Deflater} has been subclassed and the {@code end} method is
- * overridden, uses {@code finalizer} mechanism for resource cleanup. So
- * {@code end} method can be called when the {@code Deflater} is unreachable.
- * This mechanism will be removed when the {@code finalize} method is
- * removed from {@code Deflater}.
- */
- static DeflaterZStreamRef get(Deflater owner, long addr) {
- Class<?> clz = owner.getClass();
- while (clz != Deflater.class) {
- try {
- clz.getDeclaredMethod("end");
- return new FinalizableZStreamRef(owner, addr);
- } catch (NoSuchMethodException nsme) {}
- clz = clz.getSuperclass();
- }
- return new DeflaterZStreamRef(owner, addr);
- }
-
- private static class FinalizableZStreamRef extends DeflaterZStreamRef {
- final Deflater owner;
-
- FinalizableZStreamRef (Deflater owner, long addr) {
- super(null, addr);
- this.owner = owner;
- }
-
- @Override
- void clean() {
- run();
- }
-
- @Override
- @SuppressWarnings("deprecation")
- protected void finalize() {
- owner.end();
- }
- }
}
}
--- a/src/java.base/share/classes/java/util/zip/Inflater.java Fri Oct 26 10:47:05 2018 -0500
+++ b/src/java.base/share/classes/java/util/zip/Inflater.java Fri Oct 26 14:02:31 2018 -0400
@@ -87,13 +87,6 @@
* 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 {@code Inflater} has been subclassed and the {@code end} method has been
- * overridden, the {@code end} method will be called by the finalization when the
- * inflater is unreachable. But the subclasses should not depend on this specific
- * implementation; the finalization is not reliable and the {@code finalize} method
- * is deprecated to be removed.
- *
* @see Deflater
* @author David Connelly
* @since 1.1
@@ -135,7 +128,7 @@
* @param nowrap if true then support GZIP compatible compression
*/
public Inflater(boolean nowrap) {
- this.zsRef = InflaterZStreamRef.get(this, init(nowrap));
+ this.zsRef = new InflaterZStreamRef(this, init(nowrap));
}
/**
@@ -714,25 +707,6 @@
}
}
- /**
- * Closes the decompressor when garbage is collected.
- *
- * @implSpec
- * If this {@code Inflater} has been subclassed and the {@code end} method
- * has been overridden, the {@code end} method will be called when the
- * inflater is unreachable.
- *
- * @deprecated The {@code finalize} method has been deprecated and will be
- * removed. It is implemented as a no-op. Subclasses that override
- * {@code finalize} in order to perform cleanup should be modified to use
- * alternative cleanup mechanisms and remove the overriding {@code finalize}
- * method. The recommended cleanup for compressor is to explicitly call
- * {@code end} method when it is no longer in use. If the {@code end} is
- * not invoked explicitly the resource of the compressor will be released
- * when the instance becomes unreachable,
- */
- @Deprecated(since="9", forRemoval=true)
- protected void finalize() {}
private void ensureOpen () {
assert Thread.holdsLock(zsRef);
@@ -792,43 +766,5 @@
}
}
- /*
- * If {@code Inflater} has been subclassed and the {@code end} method is
- * overridden, uses {@code finalizer} mechanism for resource cleanup. So
- * {@code end} method can be called when the {@code Inflater} is unreachable.
- * This mechanism will be removed when the {@code finalize} method is
- * removed from {@code Inflater}.
- */
- static InflaterZStreamRef get(Inflater owner, long addr) {
- Class<?> clz = owner.getClass();
- while (clz != Inflater.class) {
- try {
- clz.getDeclaredMethod("end");
- return new FinalizableZStreamRef(owner, addr);
- } catch (NoSuchMethodException nsme) {}
- clz = clz.getSuperclass();
- }
- return new InflaterZStreamRef(owner, addr);
- }
-
- private static class FinalizableZStreamRef extends InflaterZStreamRef {
- final Inflater owner;
-
- FinalizableZStreamRef(Inflater owner, long addr) {
- super(null, addr);
- this.owner = owner;
- }
-
- @Override
- void clean() {
- run();
- }
-
- @Override
- @SuppressWarnings("deprecation")
- protected void finalize() {
- owner.end();
- }
- }
}
}
--- a/src/java.base/share/classes/java/util/zip/ZipFile.java Fri Oct 26 10:47:05 2018 -0500
+++ b/src/java.base/share/classes/java/util/zip/ZipFile.java Fri Oct 26 14:02:31 2018 -0400
@@ -56,7 +56,6 @@
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.misc.JavaLangAccess;
@@ -85,13 +84,6 @@
* cleanup mechanisms such as {@link java.lang.ref.Cleaner} and remove the overriding
* {@code finalize} method.
*
- * @implSpec
- * If this {@code ZipFile} has been subclassed and the {@code close} method has
- * been overridden, the {@code close} method will be called by the finalization
- * when {@code ZipFile} is unreachable. But the subclasses should not depend on
- * this specific implementation; the finalization is not reliable and the
- * {@code finalize} method is deprecated to be removed.
- *
* @author David Connelly
* @since 1.1
*/
@@ -244,7 +236,7 @@
this.name = name;
long t0 = System.nanoTime();
- this.res = CleanableResource.get(this, file, mode);
+ this.res = new CleanableResource(this, file, mode);
PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
PerfCounter.getZipFileCount().increment();
@@ -825,44 +817,6 @@
this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0);
}
- /*
- * If {@code ZipFile} has been subclassed and the {@code close} method is
- * overridden, uses the {@code finalizer} mechanism for resource cleanup.
- * So {@code close} method can be called when the the {@code ZipFile} is
- * unreachable. This mechanism will be removed when {@code finalize} method
- * is removed from {@code ZipFile}.
- */
- static CleanableResource get(ZipFile zf, File file, int mode)
- throws IOException {
- Class<?> clz = zf.getClass();
- while (clz != ZipFile.class && clz != JarFile.class) {
- if (JLA.getDeclaredPublicMethods(clz, "close").size() != 0) {
- return new FinalizableResource(zf, file, mode);
- }
- clz = clz.getSuperclass();
- }
- return new CleanableResource(zf, file, mode);
- }
-
- static class FinalizableResource extends CleanableResource {
- ZipFile zf;
- FinalizableResource(ZipFile zf, File file, int mode)
- throws IOException {
- super(file, mode);
- this.zf = zf;
- }
-
- @Override
- void clean() {
- run();
- }
-
- @Override
- @SuppressWarnings("deprecation")
- protected void finalize() throws IOException {
- zf.close();
- }
- }
}
/**
@@ -891,25 +845,6 @@
}
}
- /**
- * Ensures that the system resources held by this ZipFile object are
- * released when there are no more references to it.
- *
- * @deprecated The {@code finalize} method has been deprecated and will be
- * removed. It is implemented as a no-op. 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. The recommended cleanup for ZipFile object
- * is to explicitly invoke {@code close} method when it is no longer in
- * use, or use try-with-resources. If the {@code close} is not invoked
- * explicitly the resources held by this object will be released when
- * the instance becomes unreachable.
- *
- * @throws IOException if an I/O error has occurred
- */
- @Deprecated(since="9", forRemoval=true)
- protected void finalize() throws IOException {}
-
private void ensureOpen() {
if (closeRequested) {
throw new IllegalStateException("zip file closed");
--- a/test/jdk/java/util/zip/ZipFile/FinalizeZipFile.java Fri Oct 26 10:47:05 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2010, 2014, 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/* @test
- * @bug 7007609 7009618
- * @summary Check that ZipFile objects are always collected
- * @key randomness
- */
-
-import java.io.*;
-import java.util.Random;
-import java.util.zip.*;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class FinalizeZipFile {
-
- private static final CountDownLatch finalizersDone = new CountDownLatch(3);
-
- private static class InstrumentedZipFile extends ZipFile {
-
- public InstrumentedZipFile(File f) throws Exception {
- super(f);
- System.out.printf("Using %s%n", f.getPath());
- }
- @Override
- protected void finalize() throws IOException {
- System.out.printf("Killing %s%n", getName());
- super.finalize();
- finalizersDone.countDown();
- }
- }
-
- private static void makeGarbage() throws Throwable {
- final Random rnd = new Random();
- // Create some ZipFiles.
- // Find some .jar files in test directory.
- final File testdir = new File(System.getProperty("test.src", "."));
- check(testdir.isDirectory());
- final File[] jars = testdir.listFiles(
- new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.endsWith(".jar");}});
- check(jars.length > 1);
-
- new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]).close();
- new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]).close();
-
- // Create a ZipFile and get an input stream from it
- for (int i = 0; i < jars.length + 10; i++) {
- ZipFile zf = new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]);
- ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF");
- if (ze != null) {
- InputStream is = zf.getInputStream(ze);
- break;
- }
- }
- }
-
- public static void realMain(String[] args) throws Throwable {
- makeGarbage();
- while (!finalizersDone.await(10, TimeUnit.MILLISECONDS)) {
- System.gc();
- }
- // Not all ZipFiles were collected?
- equal(finalizersDone.getCount(), 0L);
- }
-
- //--------------------- Infrastructure ---------------------------
- static volatile int passed = 0, failed = 0;
- static void pass() {passed++;}
- static void fail() {failed++; Thread.dumpStack();}
- static void fail(String msg) {System.out.println(msg); fail();}
- static void unexpected(Throwable t) {failed++; t.printStackTrace();}
- static void check(boolean cond) {if (cond) pass(); else fail();}
- static void equal(Object x, Object y) {
- if (x == null ? y == null : x.equals(y)) pass();
- else fail(x + " not equal to " + y);}
- public static void main(String[] args) throws Throwable {
- try {realMain(args);} catch (Throwable t) {unexpected(t);}
- System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
- if (failed > 0) throw new AssertionError("Some tests failed");}
-}
--- a/test/jdk/java/util/zip/ZipFile/TestCleaner.java Fri Oct 26 10:47:05 2018 -0500
+++ b/test/jdk/java/util/zip/ZipFile/TestCleaner.java Fri Oct 26 14:02:31 2018 -0400
@@ -31,8 +31,6 @@
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.zip.*;
import jdk.internal.vm.annotation.DontInline;
import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -56,34 +54,6 @@
}
}
- private static class SubclassedInflater extends Inflater {
- CountDownLatch endCountDown;
-
- SubclassedInflater(CountDownLatch endCountDown) {
- this.endCountDown = endCountDown;
- }
-
- @Override
- public void end() {
- super.end();
- endCountDown.countDown();
- }
- }
-
- private static class SubclassedDeflater extends Deflater {
- CountDownLatch endCountDown;
-
- SubclassedDeflater(CountDownLatch endCountDown) {
- this.endCountDown = endCountDown;
- }
-
- @Override
- public void end() {
- super.end();
- endCountDown.countDown();
- }
- }
-
// verify the "native resource" of In/Deflater has been cleaned
private static void testDeInflater() throws Throwable {
Field zsRefDef = Deflater.class.getDeclaredField("zsRef");
@@ -124,44 +94,6 @@
if (cnt != 0)
throw new RuntimeException("cleaner failed to clean : " + cnt);
- // test subclassed Deflater/Inflater, for behavioral compatibility.
- // should be removed if the finalize() method is finally removed.
- var endCountDown = new CountDownLatch(20);
- for (int i = 0; i < 10; i++) {
- var def = new SubclassedDeflater(endCountDown);
- def.setInput("hello".getBytes());
- def.finish();
- n = def.deflate(buf1);
-
- var inf = new SubclassedInflater(endCountDown);
- inf.setInput(buf1, 0, n);
- n = inf.inflate(buf2);
- if (!"hello".equals(new String(buf2, 0, n))) {
- throw new RuntimeException("compression/decompression failed");
- }
- }
- while (!endCountDown.await(10, TimeUnit.MILLISECONDS)) {
- System.gc();
- }
- if (endCountDown.getCount() != 0)
- throw new RuntimeException("finalizer failed to clean : " +
- endCountDown.getCount());
- }
-
- private static class SubclassedZipFile extends ZipFile {
- CountDownLatch closeCountDown;
-
- SubclassedZipFile(File f, CountDownLatch closeCountDown)
- throws IOException {
- super(f);
- this.closeCountDown = closeCountDown;
- }
-
- @Override
- public void close() throws IOException {
- closeCountDown.countDown();
- super.close();
- }
}
@DontInline
@@ -198,27 +130,6 @@
}
}
- @DontInline
- private static void openAndCloseSubZipFile(File zip, CountDownLatch closeCountDown)
- throws Throwable {
- try {
- try (var fos = new FileOutputStream(zip);
- var zos = new ZipOutputStream(fos)) {
- zos.putNextEntry(new ZipEntry("hello"));
- zos.write("hello".getBytes(US_ASCII));
- zos.closeEntry();
- }
- var zf = new SubclassedZipFile(zip, closeCountDown);
- var es = zf.entries();
- while (es.hasMoreElements()) {
- zf.getInputStream(es.nextElement()).read();
- }
- es = null;
- zf = null;
- } finally {
- zip.delete();
- }
- }
private static void testZipFile() throws Throwable {
File dir = new File(System.getProperty("test.dir", "."));
@@ -241,16 +152,5 @@
throw new RuntimeException("cleaner failed to clean zipfile.");
}
}
-
- // test subclassed ZipFile, for behavioral compatibility.
- // should be removed if the finalize() method is finally removed.
- var closeCountDown = new CountDownLatch(1);
- openAndCloseSubZipFile(zip, closeCountDown);
- while (!closeCountDown.await(10, TimeUnit.MILLISECONDS)) {
- System.gc();
- }
- if (closeCountDown.getCount() != 0)
- throw new RuntimeException("finalizer failed to clean : " +
- closeCountDown.getCount());
}
}