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

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Jan 6 10:43:55 UTC 2025


Clarification:

`segment` and `segment2` are equals (as in `segment.equals(segment2)` 
returns true).

They feature the same lifetime, so they can be compared. But `segment2` 
is a fresh new instance which points at the same memory as `segment`.

Back to the original question: Sergio, can you please clarify what you 
mean by "copy" ? The examples Remi provided do not really copy anything, 
they just alternate between a memory segment and a byte buffer view of 
the same region of memory.

It would be useful to have a pointer to some existing code using Unsafe, 
so that we can better understand each other.

Cheers
Maurizio

On 27/12/2024 14:33, Remi Forax wrote:
>
>
> ------------------------------------------------------------------------
>
>     *From: *"Remi Forax" <forax at univ-mlv.fr>
>     *To: *"Sergio Selos" <sergio.selos.chicago at gmail.com>
>     *Cc: *"panama-dev" <panama-dev at openjdk.org>
>     *Sent: *Friday, December 27, 2024 2:52:17 PM
>     *Subject: *Re: Garbage problem migrating from sun.misc.Unsafe to
>     FFM API
>
>     I do not think you need pooling,
>     if the ByteBuffer is created from a MemorySegment, it keeps a
>     reference to the MemeorySegment
>
>     var segment =Arena./global/().allocate(8192);
>     var buffer =segment.asByteBuffer();
>
>     ...
>
>     var segment2 =MemorySegment.ofBuffer(buffer);
>     assertSame(segment, segment2);  // should be ok !
>
>
> It's not :( t's a MemorySegment with the same address but not the same 
> memory segment,
> so you have to hope that the VM is able to do escape analysis on the 
> result of ofBuffer().
>
> Rémi
>
>
>
>     ------------------------------------------------------------------------
>
>         *From: *"Sergio Selos" <sergio.selos.chicago at gmail.com>
>         *To: *"Remi Forax" <forax at univ-mlv.fr>
>         *Cc: *"panama-dev" <panama-dev at openjdk.org>
>         *Sent: *Friday, December 27, 2024 1:12:52 PM
>         *Subject: *Re: Garbage problem migrating from sun.misc.Unsafe
>         to FFM API
>
>         Thanks for your answer, Remi.
>         > Then I think that the idea if you want to minimize the
>         garbage is to create the ByteBuffer from a MemorySegment with
>         asByteBuffer() so you can then use ofBuffer() when you want
>         the MemorySegment back.
>
>         That approach could work, but ideally, we want the same
>         capability provided by sun.misc.Unsafe today: the ability to
>         work directly with external ByteBuffers. Note that we’re not
>         really using the ByteBuffer itself but its underlying native
>         memory address. Perhaps I’m overlooking something, but it
>         seems the FFM API should offer a way to copy data directly
>         between native memory regions.
>
>         I also posted this question on StackOverflow and it was
>         suggested to pool a MemorySegment per ByteBuffer.
>         Unfortunately I'm afraid that will be a show-stopper due to
>         the extra lookup in the critical path. For the record, the SO
>         question is
>         https://stackoverflow.com/questions/79311345/is-it-possible-to-copy-from-native-memory-to-a-bytebuffer-using-the-new-ffm-api
>
>         All the best,
>
>         -Sergio
>
>         On Fri, Dec 27, 2024 at 8:16 AM Remi Forax <forax at univ-mlv.fr>
>         wrote:
>
>             Hello,
>             are you using an older jdk than 22,
>             MemorySegment.ofBuffer() should only takes a ByteBuffer [1] ?
>
>             Then I think that the idea if you want to minimize the
>             garbage is to create the ByteBuffer from a MemorySegment
>             with asByteBuffer() so you can then use ofBuffer() when
>             you want the MemorySegment back.
>
>             That said, i would love to have methods like read() and
>             write() to take MemorySegment instead of ByteBuffer
>             because for my students the ByteBuffer API is an useless
>             complication (you have to understand how the indexes work)
>             when doing serious IO. Having API closest to the C ones
>             would help a lot.
>
>             Rémi
>
>             [1]
>             https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/MemorySegment.html#ofBuffer(java.nio.Buffer)
>
>             ------------------------------------------------------------------------
>
>                 *From: *"Sergio Selos" <sergio.selos.chicago at gmail.com>
>                 *To: *"panama-dev" <panama-dev at openjdk.org>
>                 *Sent: *Friday, December 27, 2024 2:05:24 AM
>                 *Subject: *Garbage problem migrating from
>                 sun.misc.Unsafe to FFM API
>
>                 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/f574223c/attachment-0001.htm>


More information about the panama-dev mailing list