Garbage problem migrating from sun.misc.Unsafe to FFM API
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Mon Jan 6 11:34:46 UTC 2025
Could also be related to this:
https://bugs.openjdk.org/browse/JDK-8343188
Which got fixed recently, and addressed a similar concern with excessive
garbage created by `ofBuffer`.
Maurizio
On 06/01/2025 11:25, Jorn Vernee wrote:
> 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