Garbage problem migrating from sun.misc.Unsafe to FFM API

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Jan 6 11:34:46 UTC 2025


Could also be related to this:

https://bugs.openjdk.org/browse/JDK-8343188

Which got fixed recently, and addressed a similar concern with excessive 
garbage created by `ofBuffer`.

Maurizio

On 06/01/2025 11:25, Jorn Vernee wrote:
> Hello,
>
> Have you verified that garbage is actually being generated when using 
> FFM?
>
> If I set up a test program based on your snippet [1], and run it with 
> -XX:CompileCommand=TraceEscapeAnalysis,... I don't actually see any 
> escaping allocations.
>
> Jorn
>
> [1]:
>
> import java.lang.foreign.Arena;
> import java.lang.foreign.MemorySegment;
> import java.nio.ByteBuffer;
>
> public class TestOfBuffer {
>
>     long address = 0;
>     MemorySegment segment;
>
>     public static void main(String[] args) {
>         TestOfBuffer recv = new TestOfBuffer();
>         recv.segment = Arena.global().allocate(100);
>
>         ByteBuffer bb = ByteBuffer.allocateDirect(10);
>         for (int i = 0; i < 20_000; i++) {
>             recv.payload(0, bb, 10);
>             bb.flip();
>         }
>     }
>
>     public void payload(long address, ByteBuffer dst, int len) {
>         if (!dst.isDirect()) {
>             throw new RuntimeException("getByteBuffer can only take a 
> direct byte buffer!");
>         }
>
>         long offset = address - this.address; // offset in our 'segment'
>
>         // Wrap the destination ByteBuffer in a temporary segment
>         // This segment's data starts at dst.position()
>         MemorySegment dstSegment = MemorySegment.ofBuffer(dst); // 
> <====== temp memory segment object
>
>         // Copy from 'segment' at 'offset' → dstSegment at offset 0 → 
> length = 'len'
>         MemorySegment.copy(this.segment, offset, dstSegment, 0, len);
>         dst.position(dst.position() + len);
>     }
> }
>
> On 27-12-2024 02:05, Sergio Selos wrote:
>> Hello,
>>
>> We have a project using sun.misc.Unsafe that we are migrating to the 
>> new FFM API. The project is at 
>> https://www.github.com/coralblocks/CoralRing
>>
>> For our particular systems (mostly financial low latency systems) it 
>> is important not to create any garbage (temp discarded instances) 
>> when receiving a message.
>>
>> We encountered an issue when copying from native memory straight into 
>> a direct ByteBuffer using the FFM API.
>>
>> Using sun.misc.Unsafe, copying from memory to a ByteBuffer can be 
>> done without creating temporary objects and garbage collector 
>> overhead. However, with the FFM API, achieving the same does not seem 
>> currently possible without generating garbage via a call to 
>> MemorySegment.ofBuffer.
>>
>> Does the FFM dev team plan to address this issue and provide a 
>> garbage-free way to perform this copy, like we currently can with 
>> sun.misc.Unsafe?
>>
>> Maybe there is already a way and we are just unaware how to do it.
>>
>> Below the current code we have with sun.misc.Unsafe, which does not 
>> produce any garbage, and the only way we are aware of to do it with 
>> the FFM API, which produces garbage.
>>
>> Thanks and let me know if you need any more details about this and/or 
>> if I can help with anything.
>>
>> All the best,
>>
>> -Sergio
>>
>> // With sun.misc.Unsafe:
>> @Override
>> public void getByteBuffer(long address, ByteBuffer dst, int len) {
>>     if (!dst.isDirect()) {
>>         throw new RuntimeException("getByteBuffer can only take a 
>> direct byte buffer!");
>>     }
>>     try {
>>         long dstAddress = (long) addressField.get(dst); // get the 
>> memory address of this ByteBuffer
>>         dstAddress += dst.position(); // adjust the address for the 
>> ByteBuffer current position
>>         unsafe.copyMemory(address, dstAddress, len); // copy over
>>         dst.position(dst.position() + len); // adjust the ByteBuffer 
>> position to reflect the copy operation
>>     } catch(Exception e) {
>>         throw new RuntimeException(e);
>>     }
>> }
>>
>> // With FFM API:
>> @Override
>> public void getByteBuffer(long address, ByteBuffer dst, int len) {
>>     if (!dst.isDirect()) {
>>         throw new RuntimeException("getByteBuffer can only take a 
>> direct byte buffer!");
>>     }
>>
>>     long offset = address - this.address; // offset in our 'segment'
>>
>>     try (Arena arena = Arena.ofConfined()) { // <==== probably 
>> creating a temp object here too
>>         // Wrap the destination ByteBuffer in a temporary segment
>>         // This segment's data starts at dst.position()
>>         MemorySegment dstSegment = MemorySegment.ofBuffer(dst, 
>> arena); // <====== temp memory segment object
>>
>>         // Copy from 'segment' at 'offset' → dstSegment at offset 0 → 
>> length = 'len'
>>         dstSegment.copyFrom(this.segment, offset, 0, len);
>>     }
>>     dst.position(dst.position() + len);
>> }
>>
>>
>>
>>
>>


More information about the panama-dev mailing list