Finalizer being run while class still in use (escape analysis bug)

Peter B. Kessler Peter.B.Kessler at Oracle.COM
Thu Oct 11 22:42:40 UTC 2018


The JavaDoc for `Reference.reachabilityFence(Object ref)` [http://hg.openjdk.java.net/jdk/jdk/file/62523934374c/src/java.base/share/classes/java/lang/ref/Reference.java#l509] says

	@param ref the reference. If {@code null}, this method has no effect.

It might be true that if the actual parameter is a constant `null` the method has no effect.  I understand the intent of the method if applied to a simple variable (e.g., `this`).  What is the effect of calling the method with a complex expression that yields an `Object`?  How does the compiler know which lifetime to extend?  When is the actual parameter evaluated?  What if the complex expression yields `null`?  Does the method still have "no effect"?

The try-with-resource solution also operates on expressions, but there it is more obvious what to make a strong reference to to keep a particular Object reachable.

Hans's `@Cleaned` annotation can only be applied to fields, not expressions.  Does that make it less useful?

			... peter

On 10/11/18 12:56 PM, Hans Boehm wrote:
> On Wed, Oct 10, 2018 at 10:42 PM Erik Osterlund <erik.osterlund at oracle.com>
> wrote:
>>
>> Hi Hans,
>>
>> Would users be happier if e.g. try with resources could be relied upon
>> having things reachable in a scope for this pattern?
>>
>> try (SlipperyObject x = getSlipperyObject()) {
>>    // x is strongly reachable in this scope
>> }
>> // x won’t die until here
>>
>> Seems more intuitive for me to reason about the reachability in a scope
>> than manually analysing the uses of locals.
>>
>> I suppose you would have to implement AutoCloseable to be allowed to use
>> such a construct. But a SlipperyObject class could maybe wrap that and even
>> call reachability fence in its close method for portability.
>>
>> The trailing close function might already have the same effect as
>> reachabilityFence.
>>
>> Just an idea.
>>
>> Thanks,
>> /Erik
>>
> 
> I think the easiest and most common use case is one in which a class C
> includes a field that needs to be explicitly cleaned up when an instance of
> C is dropped.
> (Unfortunately even libcore contains a bunch of more complicated patterns,
> but let's ignore those here.)
> 
> In that case, the alternatives are basically:
> 
> Current:
> 
> class C {
>    private long f; // A handle to some resource needing explicit cleaning.
>    ...
>    Foo someMethod(C that) {
>      try {
>        ... access f and that.f ...
>      } finally {
>        reachabilityFence(this);
>        reachabilityFence(that);
>     }
>     ... repeat for other methods ...
> }
> 
> IIUC, with your proposal, it becomes
> 
> class C {
>    private long f; // A handle to some resource needing explicit cleaning.
>    ...
>    Foo someMethod(C that) {
>      try (C me = this; c myThat = that) {
>        ... access f and that.f, or equivalently me.f and myThat.f ...
>     }
>     ... repeat for other methods ...
> }
> 
> That's clearly shorter by a constant factor. But to me it doesn't look
> consistent with the normal try-with-resources statement.
> 
> My alternative proposal would reduce this to:
> 
> class C {
>    @Cleaned private long f; // A handle to some resource needing explicit
> cleaning.
>    ...
>    Foo someMethod(C that) {
>          ... access f and that.f ...
>    }
>     ... Other methods also need no special treatment ...
> }
> 
> This has the advantage that the annotation is confined to one place,
> arguably where it belongs. The annotation overhead is shorter by a factor
> of O(<number of methods>). It doesn't get around the fact that this is
> really difficult to explain to a non-compiler-expert in full detail. But I
> think it's possible to quickly explain the annotation in a way that will
> make sense to most people and will generally lead them to correct code.
> 
> Full disclosure: This remains messy in some cases, notably if you let f
> escape from C by handing it to a caller. There's some argument that that
> should never happen for a properly designed API. It puts the caller into
> the position of explicitly having to reason about object lifetimes.
> Unfortunately things like this do seem to be hard to avoid ins some cases.
> Even in the messy cases, so far I've almost always found @Cleaned to be
> more convenient than explicit reachabilityFences.
> 



More information about the jdk-dev mailing list