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

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Jan 6 13:45:40 UTC 2025


If you just want to be able to copy memory from A to B, you can use the 
code you have shown, yes.

I'm not 100% sure of the use of the everything segment here. Perhaps you 
want to do that to avoid doing MemorySegment::ofBuffer. While I 
understand that, as mentioned, we have improved that factory, so I'm not 
sure the creation of the additional segment view should be problematic 
(e.g. it should be escape analyzed where it matters).

You can also create a downcall method handle for memcpy, and then pass 
to it the two pointers you want to copy. The pointers can either be 
passed as memory segments, or as plain longs, depending on how you 
describe the memcpy arguments to the Linker (of course the latter is 
more unsafe and not really recommended, but an option nevertheless).

Maurizio


On 06/01/2025 13:34, Sergio Selos wrote:
> 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); @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 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/981df513/attachment-0001.htm>


More information about the panama-dev mailing list