Finalization and dead references: another proposal
Vitaly Davidovich
vitalyd at gmail.com
Thu Dec 7 18:21:00 UTC 2017
On Thu, Dec 7, 2017 at 1:12 PM, Remi Forax <forax at univ-mlv.fr> wrote:
> ----- Mail original -----
> > De: "Vitaly Davidovich" <vitalyd at gmail.com>
> > À: "Peter Levart" <peter.levart at gmail.com>
> > Cc: "core-libs-dev" <core-libs-dev at openjdk.java.net>
> > Envoyé: Jeudi 7 Décembre 2017 18:46:41
> > Objet: Re: Finalization and dead references: another proposal
>
> > On Thu, Dec 7, 2017 at 12:28 PM Peter Levart <peter.levart at gmail.com>
> wrote:
> >
> >> Hi,
> >>
> >> On 12/07/2017 03:27 AM, Vitaly Davidovich wrote:
> >> > So kind of the opposite of WeakReference - a SuperStrongReference :).
> >> >
> >> > Kidding aside, it seems like the way you’d want to encapsulate this at
> >> the
> >> > language level is via a type that the JVM intrinsically knows about;
> in
> >> > this way it’s similar to the reference types today.
> >> >
> >> > An annotation probably does the trick when the value doesn’t escape
> from
> >> > the enclosing instance but I’ve no idea if that assumption covers
> enough
> >> > code to warrant this approach. AFAICT, if the value escapes into an
> >> > instance of another type that doesn’t annotate its field then all bets
> >> are
> >> > off.
> >> >
> >> > Having a wrapper type would at least make it harder to leak the
> native
> >> > handle vs the annotation approach. But of course the wrapper comes
> with
> >> > footprint and indirection costs. Maybe Valhalla could allow exposing
> >> some
> >> > magic value type that’s a zero-cost wrapper but preserves the type
> >> > information the JIT can track?
> >>
> >> There is a middle-ground. By (ab)using value types only and no special
> >> JIT magic.
> >>
> >> DirectBuffer(s) for example, could return the address not in a long, but
> >> in a value type like the following (using valhalla MVT speak):
> >>
> >> public __ByValue final class Address {
> >> private final long address;
> >> private final Object referent;
> >>
> >> public _ValueFactory static Address create(long address, Object
> >> referent) {
> >> Address a = __MakeDefault Address();
> >> a.address = address;
> >> a.referent = referent;
> >> return a;
> >> }
> >> }
> >>
> >>
> >> DirectByteBuffer for example, would have the following address() method:
> >>
> >> private long address;
> >>
> >> public Address address() {
> >> return Address.create(address, this);
> >> }
> >>
> >> Notice that 'address' field of Address value is encapsulated, so Java
> >> code can't access it directly nor it needs to. Native / Unsafe methods
> >> would be taking Address values instead of long(s), making sure the
> >> embedded referent is kept reachable.
> >>
> >> This is equivalent to passing the DirectBuffer reference to the native
> >> method(s) together with the address value, but enforced by the API so
> >> the programmer can not make a mistake. There's a lot of arithmetic going
> >> on with addresses, inside and outside of DirectBuffer implementations.
> >> Such arithmetic would have to be performed by the Address value type
> >> itself, making sure the results of operations on Address values are
> >> Address values that maintain the same referent. For example:
> >>
> >> public __ByValue final class Address {
> >> ...
> >> public _ValueFactory Address plus(long offset) {
> >> Address a = __MakeDefault Address();
> >> a.address = address + offset;
> >> a.referent = referent;
> >> return a;
> >> }
> >> ...
> >>
> >> So no magic here. Just API.
> >
> > This is an API version of Hans’s #3 approach. As he said, there’s
> > performance overhead and nothing guarantees that the referent is kept
> alive
> > - that’s an implementation artifact.
>
> it's not even true, the implementation do not even guarantee that the
> referent field will be kept on stack,
> a value type can disappear completely if its field are not used,
> so without a reachabilityFence somewhere, it will not work.
>
Not sure what you mean here. Even if the value type is scalar replaced
(which I'd actually hope to be the case for something like this, but that's
an aside), it doesn't change anything - that referent is passed into a
black hole (i.e. the JNI call) that, as of today, the JIT cannot reason
around. As such, the referent is kept alive across the call. That's #3
that Hans mentioned. The value type aspect doesn't change anything other
than not requiring a separate heap object to be the wrapper.
>
> >
> > I think without the VM knowing about these things intrinsically it’s not
> a
> > 100% reliable solution because it’s not concretely requesting a certain
> > behavior.
>
> here is another approach based on the Peter idea,
> what we want is that in order to access to an address, you have to send an
> object that will be kept until the end of the call because the is a call to
> reachabilityFence at the end, so
>
> class DirectByteBuffer {
> private long address;
>
> void compute(LongConsumer consumer) {
> consumer.accept(address);
> Reference.reachabilityFence(this);
> }
> }
>
>
> so with the example of Hans,
> private static native void nativeDoSomething(long nativePtr, long
> anotherNativePtr);
>
> a call to nativeDoSomething, is
> buffer1.compute(address1 -> buffer2.compute(address2 ->
> nativeDoSomething(address1, address2)));
>
> so at least in term of API it's doable, the question is how to be sure
> that the JIT will inline the whole thing.
>
It's not just inlining, but escape analysis as well (for this particular
example). And this particular API will be very much a victim of profile
pollution, and so inlining will stop once this goes megamorphic (and it
probably will). Once inlining stops, EA will most likely fall apart as
well.
>
> >
> >>
> >>
> >> Regards, Peter
> >>
> >>
> >> --
> > Sent from my phone
>
> cheers,
> Rémi
>
More information about the core-libs-dev
mailing list