JDK-8205549: unsafe and LW1: needed for MethodHandles, VarHandles and Reflection

Karen Kinnear karen.kinnear at oracle.com
Wed Jun 27 21:36:44 UTC 2018


I’ve appended a summary of Unsafe object accessors used in MethodHandles, VarHandles and Reflection.

Thanks to Remi’s hashCode that Mandy was trying, we identified a hole in our current implementation.
Turns out our tests for MethodHandles, VarHandles and Reflection did not include testing for field accessors 
to flattened value type fields, which all use unsafe.

Model of usage (see e.g. test/hotspot/jtreg/runtime/Unsafe/GetPutObject.java)
  1. use reflection to return a j.l.reflect.Field: Test.class.getField("fieldname")
  2. unsafe.objectFieldOffset(j.l.reflect.Field)
  3. unsafe.getObject(jobject t, offset)

sample array usage:
  Object arrayObject[]
  1. int scale = unsafe.arrayIndexScale(arrayObject.getClass())
  2. long offset = unsafe.arrayBaseOffset(arrayObject.getClass())
  3. iterate, starting with offset, += scale and pass to unsafe.getObject(arrayObject, offset)

Issues:
1. getObject on a flattened value field today will return the first 64 bits of the flattened contents
2. putObject on a flattened value field today will overwrite the first 64 bits with an object address
3. put$Type$ to a field in a value class will NOT prevent writing
    note: I recognize this is unsafe. I highly recommend we prevent writing when given a clazz and offset so
    we can identify a value type field.
4. getObject/putObject not only accept a clazz + offset, they also accept a null base pointer + arbitrary address
     - there is no way to determine the start of the object
5. performance
     - basic unsafe calls are going to be very slow when applied to flattened fields
6. C2 intrinsics
     - today we count on intrinsics to make these fast. If C2 scalarizes fields from a value type, there may not be any value type to return

Status:
1. Frederic has been working rapidly on an initial prototype for changing unsafe, and sent a webrev
for unsafe changes.
http://cr.openjdk.java.net/~fparain/Unsafe/webrev.02/index.html <http://cr.openjdk.java.net/~fparain/Unsafe/webrev.02/index.html>
   Note - this will not handle passing a null base pointer to a value type

2. Paul has started prototyping one possible form of optimization - 
   - add some internal APIs to determine if you have a flattened field or array
   - call a different unsafe API to access a flattened field or array element
   - use this for VarHandles

3. Mandy is prototyping optimizations for MethodHandles and Reflection to use these new APIs

Next Steps:

Roland:
    Please do look at C2 intrinsics for the unsafe Object accessors and see what it would take to make these intrinsics work
for value types
    If you think there is significant benefit in adding new APIs, please help us understand what APIs might be most helpful.

Mandy, Paul - could you possibly send your webrevs when you are ready?

Then we can have a better conversation of alternatives with our C2 folks.

We will need a more complete set of tests specifically for MethodHandles, VarHandles and Reflection as well as for
unsafe.
    

thanks,
Karen

p.s. Here is the usage of unsafe for MethodHandles, VarHandles, Reflection and java.util.Concurrent.
I did not include explicit internal uses on JDK existing classes or on ByteBuffers.
I did not do a complete search of the JDK.

MethodHandles:

DirectMethodHandles create LambdaForms with NamedFunctions in unsafe to access fields. Underneath they use unsafe:
  getObject, getObjectVolatile, putObject, putObjectVolatile as well as equivalents for primitive types.

    sources in DirectMethodHandles.java: see makePreparedFieldLambdaForm, getFieldKind: Kind is an enum in LambdaForm.java
    note: uses MethodHandleNatives.objectFieldOffset, staticFieldBase, staticFieldOffset to set up base & offset, base is always
     either the mirror for statics or the class for instances. offset is obtained from fieldDescriptor returned by LinkResolver::resolve_field,
     i.e. instanceKlass.find_field(). MemberName stores offset in vmindex, is_static, is_setter are stored in flags.

     I do not see any array element accessors via MethodHandles

VarHandles:
    Creates Forms which use unsafe.get$Type$ use:
    getObject, putObject and decorations such as Volatile, Opaque,Acquire, … as well as equivalents for primitive types

    VarHandles ALSO access array elements using unsafe.
     arrayBaseOffset, arrayIndexScale (returns ascale)
     shift = 31 - Integer.numberOfLeadingZeros(ascale) and then e.g. UNSAFE.get$Type$Volatile, etc.

    (sources in VarHandle.s.java, X-VarHandle.java.template). Acquire base and offset from MethodHandleNatives calls.
     For the non-volatile case, appears to directly get/set from the array[index]

Reflection:
    Reflection creates UnsafeFieldAccessorImpl (or Qualified for Volatile, same 2 for static) and they use unsafe:
    getObject, putObject, getObjectVolatile, putObjectVolatile

    sources in jdk/internal/reflect/UnsafeFieldAccessorImpl.java uses
    unsafe.staticFieldOffset or unsafe.objectFieldOffset

    I do not see any array element accessors in Reflection.

java.util.Concurrent: Atomics - uses various decorated versions of Get/putObjectVolatile/Release/compareAndSet etc.
I think clarifying that LW1 does not support java.util.Concurrent atomics is the way to handle this one.
     


More information about the valhalla-dev mailing list