javaunsafememory-segmentationmemory-layoutvarhandle

How to Replace Unsafe with VarHandle or Foreign API


I've had a hard time removing usages of Unsafe and replacing with either VarHandle or MemorySegment/MemoryLayout. The goal is to remove usages of Unsafe and replace with nondeprecated API's.

Two examples are:

private static long methodA(Buffer x) {    
   return UNSAFE.getLong(x, addressFieldOffset); 
}

and

private static Object methodB(ByteBuffer x) {    
   return UNSAFE.getObject(x, hbFieldOffset); 
}

Firstly, creating the right VarHandle or MemorySegment has been a challenge. It seems when I end up using the documentation's suggested "Use VarHandle.get() or MemorySegment.get(ValueLayout.ofLong, long)" I end up with a WrongMethodTypeException or a different result than before that breaks current tests, etc. Can I get some help on the appropriate use?

I've tried:

MemorySegment segment = MemorySegment.ofBuffer(x);
long result = segment.get(ValueLayout.ofLong, int offset) // I've tried several different layouts and different offsets,

either getting a WrongMethodTypeException or an incompatible offset error

Also tried:

MemorySegment segment = MemorySegment.ofArray(new long[10]); // also tried with byte[10] and int[10]

With varHandle I've tried:

VarHandle handle = MethodHandles.arrayElementVarHandle(long[].class); \\ also tried byte[].class, int[].class, and long[].class

Buffer bufferField = Buffer.allocate(16)

VarHandle handle = MethodHandles.lookup().findStaticVarHandle(ClassIAmIn.class, "bufferField", Buffer.class);

Usually get a Cannot convert MethodHandles(VarHandle, Buffer, long) to (VarHandle)Buffer or something along those lines.


Solution

  • I'm assuming that addressFieldOffset is the offset of field address of class java.nio.Buffer, and hbFieldOffset is the offset of field hb of class java.nio.ByteBuffer.

    You're trying to access private fields. That means you need a private method lookup first:

    MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Buffer.class, MethodHandles.lookup());
    

    You need to open package java.nio of module java.base to your module (ALL-UNNAMED if your module is unnamed), or you'll get an exception:

    Exception java.lang.IllegalAccessException: module java.base does not open java.nio to unnamed module @7e0b0338
          at MethodHandles.privateLookupIn (MethodHandles.java:279)
    

    Now you can lookup the var handles:

    VarHandle addressHandle = lookup.findVarHandle(Buffer.class, "address", long.class);
    long address = (long) addressHandle.get(buffer);
    System.out.printf("%d%n", address);
    
    VarHandle hbHandle = lookup.findVarHandle(ByteBuffer.class, "hb", byte[].class);
    byte[] hb = (byte[]) hbHandle.get(buffer);
    System.out.printf("%s%n", Arrays.toString(hb));