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

Jorn Vernee jorn.vernee at oracle.com
Mon Jan 6 11:25:05 UTC 2025


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