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