Is it possible to use the FFM API to "free" a direct NIO buffer?

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Oct 6 17:00:05 UTC 2023


On 06/10/2023 17:23, Julien wrote:
> Thank you for your detailed answer. I think that I see what you mean. 
> However, if I keep strong references on the created segments in the 
> factory, I will prevent the garbage collector from doing its job. If I 
> use a weak hash map to store the buffers as keys and records 
> containing arenas and segments as values, maybe it won't work as 
> expected if the segments and/or the arenas strongly refer to the 
> buffers. My main use case works as you describe, the engine creates 
> the direct NIO buffers. How would you design such a factory?

So, if you want deterministic deallocation (e.g. close), you need to get 
rid of automatic GC behavior. This means that your factory will need to 
allocate direct buffers using a confined or a shared arena (which can be 
closed). This means that you will need to figure out what the lifetime 
of the buffer/segments you create is - in some cases you might get away 
with using a single lifetime that is shared by all segments. In other 
cases, some kind of "lumping" will occur, and you will need to figure 
out how, given a buffer, to go to the corresponding arena. Or you might 
want to use a custom arena/allocator. In the end, I think it depends on 
how the segments/buffers you create are _used_ by the clients of the 
factory, so it's kind of hard for me to provide a one-size fits all 
approach.

For instance, if clients are using the buffers in a confined way, you 
could have a factory like this:

```
ByteBuffer allocateDirectCloseable(int size, Arena arena) {
     return arena.allocate(size).asByteBuffer();
}
```

And then you can just do:

```
try (Arena arena = Arena.ofConfined()) {
    ByteBuffer buffer = allocateDirectCloseable(100);
    ...
} // free buffer here!
```

In the case where you want a single arena per buffer (as you want to be 
able to free them individually) you could get there with some kind of 
record:

```
record Allocation(ByteBuffer buffer, Arena arena) {
     void close() { arena().close(); }
}

Allocation allocateDirectCloseable(int size) {
     Arena arena = Arena.ofConfined();
     return new Allocation(arena.allocate(size).asByteBuffer(), arena);
}

```

These are only some (very sketchy) ideas on how you might want to 
approach the problem. I suspect that in a realistic use case, a 
combination of approaches might be necessary, or that an application 
might want to allocate its own memory pool (with a relatively "global" 
lifetime) but then have policies by which portions of the pool can be 
shared by different clients. This approach is illustrated in an example 
in this document (see section "Custom Allocators"):

https://cr.openjdk.org/~mcimadamore/panama/why_lifetimes.html


I hope this helps.

Maurizio





More information about the panama-dev mailing list