Garbage problem migrating from sun.misc.Unsafe to FFM API
rsmogura at icloud.com
rsmogura at icloud.com
Mon Jan 6 11:56:46 UTC 2025
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);
>> }
>>
>>
>>
>>
>>
More information about the panama-dev
mailing list