RFR 9: 8138696 : java.lang.ref.Cleaner - an easy to use alternative to finalization
Roger Riggs
Roger.Riggs at Oracle.com
Thu Oct 15 16:00:57 UTC 2015
Hi Peter,
It was not the (my/our) intention for the Cleaner to be able to
completely replace finalization.
The emphasis is on keeping it simple and efficient and covering the 99%
of the cases.
On 10/15/2015 9:49 AM, Peter Levart wrote:
>
>
> On 10/15/2015 03:21 PM, Roger Riggs wrote:
>> Peter, Chris,
>>
>> Plausible but getting complicated as Chris observed.
>>
>> We can be on the lookout for specific cases in the JDK like that. I
>> expect most
>> can be resolved by specific cooperation between the super/subclasses.
>
> How? Such "cooperation" can not use the tracked object's instance
> methods as the instance is gone when cleanup happens, so no overriding
> is possible. Can you rewrite this example:
>
> class SuperClass {
> protected final Cleanup cleanup =
> XXX.getCleaner().phantomCleanup(this);
>
> SuperClass() {
> cleanup.append(() -> {... super class cleanup ...});
> }
> }
>
> class SubClass extends SuperClass {
> SubClass() {
> super();
> cleanup.prepend(() -> {... pre-actions ...})
> .append(() -> {... post-actions ...});
> }
> }
>
>
> ...using just current simple Cleaner API?
>
> I think that if we design an API to replace finalize(), then it should
> support use cases that are possible with finalize(). Not just use
> cases in the JDK itself, but also elsewhere on the planet.
I'd be interested in the other cases, but it is not a goal to solve
every finalization problem.
>
>> The cleanup of each class should *only* be doing cleanup for their
>> own class.
>> Because it is invoked after finalization, there can be *no* access to
>> the instance
>> being cleaned. Any shared state will be duplicated between the two
>> cleanable behaviors.
>> If the subclass shares state with the superclass (protected fields or
>> references)
>> then the cleanup needs to be co-designed.
>
> Even if state needed for clean-up is duplicated (it can just be doubly
> referenced, for example: superclass establishes state and exposes it
> to subclass that just references it), registering separate Cleanup
> instances per super/subclass means that you can not control the order
> of them fire-ing. If for example, the subclass cleanup logic needs the
> superclass still be fully functional, it can do that now by:
>
> @Override protected void finalize() {
> ... subclass cleanup ... // flushing something - needs
> superclass functionality
> super.finalize();
> }
>
> With separate Cleanable(s) for super/subclass, the order is not
> guaranteed.
The superclass can expose its Cleanable and additional functionality to
prepend or append.
That function do not exclusively need to be supported by the
Cleaner/Cleanable types.
The order can be ensured by registering the cleanups with each other and
delegating
to the other to achieve a desired execution order.
This would put the necessary mechanisms in the types that need
specialized behavior
without adding complexity to the Cleaner.
If it turns out that it is a frequent pattern then it would make sense
to look at it again.
Regards, Roger
>
>>
>> Canceling cleanup is easier, because the object has not been finalized.
>> If such a function is needed, it should be part of the API of the
>> superclass
>> and part of the contract.
>
> Ideally such methods would be in superclass only and be final and just
> delegate to Cleanable.clear() or Cleanable.clean(). The Cleanable
> should be the only place where the cleanup logic is encapsulated.
>
>>
>> Also, I've seen a few calls to super.finalize() where there were no
>> finalizers
>> in any of the superclasses. It would be considered good design to
>> always include it.
>> I don't know if the optimization for empty finalize methods includes the
>> case where it only calls super.finalize().
>
> Ah, I forgot that empty finalize() is optimized away (the Object is
> not even registered for finalization).
>
> Regards, Peter
>
>>
>> Roger
>>
>> On 10/15/15 7:43 AM, Chris Hegarty wrote:
>>> Peter,
>>>
>>> On 15 Oct 2015, at 09:12, Peter Levart <peter.levart at gmail.com> wrote:
>>>
>>>> On 10/14/2015 07:43 PM, Roger Riggs wrote:
>>>>> Hi Alan, Mandy,
>>>>>
>>>>> I looked at a few of the many uses of finalize and the likely
>>>>> changes.
>>>>> The zip Inflater and Deflater are relatively simple cases.
>>>>> Some finalizers are not used and can be removed.
>>>>> The sun.net.www.MeteredStream example subclasses PhantomCleanable
>>>>> to add the state and cleanup
>>>>> behavior.
>>>>>
>>>>> http://cr.openjdk.java.net/~rriggs/webrev-cleaning-finalizers/
>>>>>
>>>>> Some of the harder cases will take more time to disentangle the
>>>>> cleanup code.
>>>>> For example, ZipFile, and FileIn/OutputStream (Peter has
>>>>> prototyped this).
>>>>>
>>>>> Roger
>>>> Hi Roger,
>>>>
>>>> It's good to see some actual uses of the API and how it is supposed
>>>> to be used in migration from finalize() methods. I think empty
>>>> protected finalize() method is almost always safe to remove. If the
>>>> class is not subclassed, it serves no purpose (unless some other
>>>> same-package class or itself is calling it, which can be checked
>>>> and those calls removed). If subclass overrides finalize() and
>>>> calls super.finalize(), its ok (it will call Object.finalize then
>>>> when empty finalize() is removed). The same holds if a subclass
>>>> calls finalize() as a virtual method regardless of whether it also
>>>> overrides it or not.
>>>>
>>>> One thing to watch for is in case a subclass overrides finalize()
>>>> like this:
>>>>
>>>> class Subclass extends Superclass {
>>>> ...
>>>> @Override protected finalize() {
>>>> .... pre-actions ...
>>>> super.finalize();
>>>> ... post-actions...
>>>> }
>>>>
>>>> ... where the order of cleanup actions has to be orchestrated
>>>> between super and subclass. Having a PhantomCleanable replace the
>>>> finalize() in a superclass has a similar effect as the following
>>>> re-ordering in subclass:
>>>>
>>>> @Override protected finalize() {
>>>> .... pre-actions ...
>>>> ... post-actions...
>>>> super.finalize();
>>>> }
>>>>
>>>> ...since finalization is performed before PhantomReference is
>>>> enqueued. This re-ordering is luckily often safe as post-actions
>>>> usually can't use superclass resources any more and usually don't
>>>> depend on the state of superclass. In addition, when superclass
>>>> actions do happen, they can't invoke any instance methods if they
>>>> are refactored to use Cleaner.
>>>>
>>>> This brings up an interesting question. finalize() method allows
>>>> subclasses to override it and augment cleanup logic to include any
>>>> state changes or resources used by subclass.
>>> Or for a subclass to effectively cancel any clean up, by
>>> providing an empty finalize() method. Which I think is
>>> also supported by your proposal, or at least a side-effect
>>> of having the Cleanup as a protected field ( you can call
>>> clear on it, right? ).
>>>
>>> Having the Cleanup as a protected field looks a little odd,
>>> but no more so than the public/protected finalize method.
>>>
>>> This is now getting even more complicated. There are
>>> potentially multiple object references being tracked as
>>> part of the cleanup of a single “significant” object ?
>>>
>>> -Chris.
>>>
>>>> How about Cleanup API? Subclass can register it's own Cleanable for
>>>> own resources, but order of execution of superclass and subclass
>>>> Cleanable(s) is arbitrary then. Cleanables will often be
>>>> established in constructors and super/subclass constructors have a
>>>> defined order of execution. So what about the following:
>>>>
>>>> public class Cleaner {
>>>>
>>>> public Cleanup phantomCleanup(Object referent);
>>>>
>>>> public interface Cleanable {
>>>> void clean();
>>>> void clear();
>>>> }
>>>>
>>>> public interface Cleanup extends Cleanable {
>>>> Cleanable append(Runnable action);
>>>> Cleanable prepend(Runnable action);
>>>> }
>>>>
>>>> public static abstract class PhantomCleanable extends
>>>> PhantomReference implements Cleanable { ... }
>>>>
>>>> private static final class PhantomCleanup extends PhantomCleanable
>>>> implements Cleanup { ... }
>>>>
>>>> ...use...
>>>>
>>>> class SuperClass {
>>>> protected final Cleanup cleanup =
>>>> XXX.getCleaner().phantomCleanup(this);
>>>>
>>>> SuperClass() {
>>>> cleanup.append(() -> {... super class cleanup ...});
>>>> }
>>>> }
>>>>
>>>> class SubClass extends SuperClass {
>>>> SubClass() {
>>>> super();
>>>> cleanup.prepend(() -> {... pre-actions ...})
>>>> .append(() -> {... post-actions ...});
>>>> }
>>>> }
>>>>
>>>>
>>>> Regards, Peter
>>>>
>>>>>
>>>>>
>>>>> On 10/14/2015 10:23 AM, Alan Bateman wrote:
>>>>>> On 14/10/2015 15:03, Roger Riggs wrote:
>>>>>>> Hi Alan,
>>>>>>>
>>>>>>> So any user of the Cleaner can take advantage of the mechanism,
>>>>>>> for example in a different package or module.
>>>>>>> For example, Netbeans.
>>>>>> Cleaner + Cleanable need to be public of course so maybe we
>>>>>> should wait for the examples that extend WeakCleanableRef or cast
>>>>>> the Cleanable to a WeakCleanableRef before seeing if this is the
>>>>>> right thing or not.
>>>>>>
>>>>>> -Alan
>>
>
More information about the core-libs-dev
mailing list