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

Sergio Selos sergio.selos.chicago at gmail.com
Mon Jan 6 13:34:11 UTC 2025


Hi Everyone,

Thanks for taking the time to analyze and reply to my question. I think the
classic C memcpy would solve my problem, and I could do it through some JNI
code, but I would be happy to be able to use Panama (pure Java) instead. I
posted this question on SO and someone has suggested the solution below
using Panama. I haven't tried it yet.

https://stackoverflow.com/a/79311822/28712954

// With FFM API:static final MemorySegment EVERYTHING_SEGMENT =
MemorySegment.NULL.reinterpret(Long.MAX_VALUE);
@Overridepublic void getByteBuffer(long address, ByteBuffer dst, int len) {
    if (!dst.isDirect()) {
        throw new RuntimeException("getByteBuffer can only take a
direct byte buffer!");
    }

    long dstAddress = (long) addressField.get(dst) + dst.position();
    EVERYTHING_SEGMENT.copyFrom(this.segment, offset, dstAddress, len);
    dst.position(dst.position() + len);
}

All the best,

-Sergio


On Mon, Jan 6, 2025 at 6:57 AM <rsmogura at icloud.com> wrote:

> Hi,
>
> I wonder if this concern can be because of using Arena arena =
> Arena.ofConfined(), or due to @Overriding method and inability to inline
> method.
>
> What do you think (sorry if I missed parts of this discussion).
>
> Best regards,
> Rado
>
> > Wiadomość napisana przez Jorn Vernee <jorn.vernee at oracle.com> w dniu 6
> sty 2025, o godz. 12:25:
> >
> > 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);
> >> }
> >>
> >>
> >>
> >>
> >>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20250106/c2baf101/attachment-0001.htm>


More information about the panama-dev mailing list