JDK-8187485 : Update Zip implementation to use Cleaner, not finalizers
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.util.jar
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Submitted: 2017-09-12
  • Updated: 2017-12-08
  • Resolved: 2017-12-08
Related Reports
CSR :  
Relates :  
Description
Summary
-------

To replace the finalizer mechanism with java.lang.ref.Cleaner to close the object java.util.zip.ZipFile/Inflater/Deflater, and cleanup/release its underlying native resources when the object is no longer reachable.

Problem
-------

The finalization mechanism has a history of usage and performnace issues, it is desirable the j.u.zip implementation to migrate away from its use of finalizer to the newly introduced java.lang.ref.Cleaner.

Solution
--------

1) To mark the overridden finalize() method of ZipFile/Deflater/Inflater class as "@Deprecated(, forRemoval=true)" and implement  it with an empty body (to leverage the VM feature that vm does not put an object into the finalization process if its finalize() has an empty-body-ed implementation). The new implementation uses Cleaner to do the resource cleanup when the object has become unreachable, with the exception when the ZipFile/Inflater/Deflater has been subclassed and its corresponding close()/end() method has been overridden. In this case, the finalization mechanism is used to do the cleanup, and for behavioral compatibility, the corresponding close()/end() in subclass method will be called when the ZipFile/Inflater/Deflater object is unreachable.

2) To update the specification of the end() method (for both Deflater and Inflater) to remove the requirement that the end() "will be called by the finalizer() automatically".

3) To add additional @apiNote in class spec and @implSpec in finalize() method spec to clarify (a) the recommended approach to cleanup the resource after use, and (b) there is no guarantee that the close()/end() method will be called directly by finalize(), though the method in subclass will be called after the object is unreachable, via an implementation specific mechanism, for behavioral compatibility.

http://cr.openjdk.java.net/~sherman/8185582/webrev

Specification
-------------


src/java.base/share/classes/java/util/zip/Deflater.java

[adding class spec]
```
+ * @apiNote
+ * To release resources used by this {@code Deflater}, the {@link #end()} method
+ * should be called explicitly. 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 {@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.
+ *
```
[updating end() spec]
```
     /**
      * Closes the compressor and discards any unprocessed input.
+     *
      * This method should be called when the compressor is no longer
-     * being used, but will also be called automatically by the
-     * finalize() method. Once this method is called, the behavior
-     * of the Deflater object is undefined.
+     * being used. Once this method is called, the behavior of the
+     * Deflater object is undefined.
+     *
      */
     public void end() {...}
```
[updating finalize() spec]
```
     /**
      * Closes the compressor when garbage is collected.
      *
-     * @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.
-     */
-    @Deprecated(since="9")
-    protected void finalize() {
-        end();
-    }
+     * @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() {}
```

src/java.base/share/classes/java/util/zip/Inflater.java

[adding class spec]
```
+ * @apiNote
+ * To release resources used by this {@code Inflater}, the {@link #end()} method
+ * should be called explicitly. 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 {@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.
+ *
```
[updating end() spec]
```
     /**
      * Closes the decompressor and discards any unprocessed input.
+     *
      * This method should be called when the decompressor is no longer
-     * being used, but will also be called automatically by the finalize()
-     * method. Once this method is called, the behavior of the Inflater
-     * object is undefined.
+     * being used. Once this method is called, the behavior of the
+     * Inflater object is undefined.
+     *
      */
     public void end() {
```
[updating finalize() spec]
```
      /**
      * Closes the decompressor when garbage is collected.
      *
-     * @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.
-     */
-    @Deprecated(since="9")
-    protected void finalize() {
-        end();
-    }
+     * @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() {}
```

src/java.base/share/classes/java/util/zip/ZipFile.java

[adding class spec]
```
+ * @apiNote
+ * To release resources used by this {@code ZipFile}, the {@link #close()} method
+ * should be called explicitly 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 {@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.
+ *
```
[updating finalize() spec]
```
     /**
      * Ensures that the system resources held by this ZipFile object are
      * released when there are no more references to it.
      *
-     * <p>
-     * Since the time when GC would invoke this method is undetermined,
-     * it is strongly recommended that applications invoke the {@code close}
-     * method as soon they have finished accessing this {@code ZipFile}.
-     * This will prevent holding up system resources for an undetermined
-     * length of time.
-     *
-     * @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.
+     * @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
-     * @see    java.util.zip.ZipFile#close()
      */
-    @Deprecated(since="9")
-    protected void finalize() throws IOException {}
```
Comments
Moving amended request to Approved.
08-12-2017

A reminder, the CSR request will stay in a pended state unless there is more discussion or a revised proposal from the CSR assignee.
17-11-2017

Sorry for the belated comments. Similar reaction as to JDK-8187325, I think that removing the finalize method without first having the method be deprecated-for-removal for at least one release is too aggressive. Making the request as pended.
01-11-2017