Finalization and dead references: another proposal

Hans Boehm hboehm at google.com
Fri Dec 8 00:57:51 UTC 2017


On Thu, Dec 7, 2017 at 7:49 AM, Peter Levart <peter.levart at gmail.com> wrote:

> ...
> 1st case that comes to mind and is a real case in the JDK is the existence
> of sun.nio.ch.DirectBuffer internal (not exported) interface implemented by
> NIO direct buffer(s) with method:
>
>     long address();
>
> Utility method like the following:
>
>     static void erase(ByteBuffer bb) {
>         unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(),
> (byte)0);
>     }
>
> ...won't get reachabilityFence(bb) before return, will it? Calling a
> method on a bb doesn't count as a dereference of a reachablity-sensitive
> field of bb. Unless methods could also be annotated with the same
> annotation. This would only work for instance methods that return a native
> resource kept in the same instance or some instance that is reachable from
> this instance.
>
> Native resources are typically encapsulated, but there are various levels
> of encapsulation. DirectBuffer is an example of module level encapsulation
> by non-exported public interface implemented by package-private classes.
>

I think there are two issues here. The first one is the use of a getter to
retrieve the value of the address field. This yields a value whose logical
lifetime is limited by the lifetime of a separate garbage-collected Java
object. That feels very un-Java-like and dangerous. My immediate
inclination is to strongly discourage this idiom.

A second issue is a possible access to an @ReachabilitySensitive field from
outside the declaring class. I've gone back and forth as to whether they
should be covered, i.e. result in a logical reachabilityFence(bb). It's
easy enough to specify either way. The major difference is whether an
implementation that simply avoids doing dead reference elimination on any
class containing an @ReachabilitySensitive annotation is conforming. If you
want this to work, the criterion becomes "accesses
an @ReachabilitySensitive field". Which may be OK?


> 2nd case is artificial. But, since we have streams, lambdas, optionals...
> It's easy to fool above rules even if the annotation is put on the
> DirectBuffer#address() method ...
>
> static void doSomethingWith1stNonNull(DirectBuffer... dbs) {
>     Stream.of(dbs)
>         .filter(Objects::nonNull)
>         .mapToLong(DirectBuffer::address)
>         .findFirst()
>         .ifPresent(addr -> {
>             ... do something with addr ...
>         });
> }
>
> Even with imperative programming, one can play with scopes:
>
> static void doSomethingWith1stNonNull(DirectBuffer... dbs) {
>     long addr = 0;
>     for (DirectBuffer db : dbs) {
>         if (db != null) {
>             addr = db.address();
>             break;
>         }
>     }
>     if (addr != 0) {
>         ... do something with addr ...
>     }
> }
>

Aside from the above issues, the intent was for these to work.   A
rechabilityFence(dbs) should be generated at the end of either method
(possibly among others).

>
> Or, hope that this would work (but the above rules don't cover it):
>
> static void doSomethingWithEither(DirectBuffer db1, DirectBuffer db2) {
>     long addr = ((some condition) ? db1 : db2).address();
>     ... do something with addr ...
> }
>
> Again, this should be covered, aside from the issues discussed above. If
this were an access to an @ReachabilitySensitive field, a
reachaibilityFence for the appropriate dbX is required at the end. A real
compiler would just generate one each for both.


> But I can imagine that teaching users to not do such foolish things would
> be easy. The rules are simple:
> - always dereference reachablity-sensitive resources directly through
> local reference variables.
> - make sure that the local reference variable that you extracted
> reachablity-sensitive resource from, is in scope while you perform
> operations on the resource.
>
> Regards, Peter
>
>
>


More information about the core-libs-dev mailing list