On Thu, 20 Aug 2026 12:25:14 GMT, Per Minborg <[email protected]> wrote:

>> ## Summary
>> 
>> This PR proposes to introduce a pooled confined arena as an optimization for 
>> `Arena.ofConfined()`, where small native allocations can be served from a 
>> reusable per-thread memory pool instead of calling the regular native 
>> allocator for every short-lived arena. The arena remains confined to its 
>> owner thread and is still closed normally, but its backing storage can be 
>> reset and reused when the arena closes. The feature requires no API changes.
>> 
>> ### Outline
>> 
>> Platform threads: There are up to four lazily allocated pools per Thread, 
>> encoded in `Thread.FieldHolder.confinedMemoryPool`.
>> Virtual threads: Works in the same way but uses its _carrier thread's _ 
>> cache instead.
>> 
>> Pooled memory is zeroed out upon _closing_ an Arena to minimize data 
>> visibility between reuse. This means the data is visible only within a TWR 
>> block, and never outside it.
>> 
>> A confined arena has access to four pools, each of size 64 bytes.  The pool 
>> sizes are configurable via a system property and can be 8, 16, 32, or 64 
>> bytes. Pooling can also be turned off completely by setting the pool 
>> power-of-two size to zero. As there can be up to four pools per thread, 
>> nested confined arenas are supported (i.e., up to four nested arenas).
>> 
>> ## Static Analysis
>> 
>> An extensive static corpus analysis of third-party libraries and the JDK 
>> itself has been conducted with respect to `Area.ofConfined()` usage, 
>> revealing that confined arenas were used _only_ in TWR blocks and _never_ in 
>> an unstructured way. The static analysis further revealed that in most 
>> cases, only a small amount of native memory was ever allocated, usually less 
>> than 32 bytes, and in many cases, 8 bytes or less. This usage pattern lends 
>> itself well to pooling. 
>> 
>> ## Dynamic Analysis
>> 
>> A dynamic statistical analysis of actual runs was also made, where various 
>> properties of confined arenas were recorded and summarized during a complete 
>> tier1 test run. While a tier1 run is not necessarily representative of a 
>> typical application workload, it provided some interesting results:
>> 
>> The run produced 93 per-process histogram blocks and 788,773,092 closed 
>> confined arenas. The result is dominated by arenas with no native allocation 
>> at all: 375,934,768 arenas (47.661%) are in the zero-byte bucket. Counting 
>> arenas up to 63 bytes covers 99.997% of all arena closures.
>> 
>> The largest count bucket is 8-15 bytes per arena with 400,951,293 arenas 
>> (50.832% of all arenas). The largest byte bucket is 8-15 bytes per ar...
>
> Per Minborg has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Allow negative addresses and improve initial zeroing

I've fetched the branch and played around a bit. IMHO this benchmark is really 
impressive:


StrLenTest.panama_strlen_alloc        5  avgt    9   16.237 ± 0.230  ns/op
StrLenTest.panama_strlen_alloc       20  avgt    9   16.181 ± 0.230  ns/op
StrLenTest.panama_strlen_alloc      100  avgt    9   38.858 ± 1.470  ns/op
StrLenTest.panama_strlen_alloc      451  avgt    9   51.227 ± 2.423  ns/op

StrLenTest.panama_strlen_pool         5  avgt    9   15.666 ± 0.991  ns/op
StrLenTest.panama_strlen_pool        20  avgt    9   16.078 ± 0.768  ns/op
StrLenTest.panama_strlen_pool       100  avgt    9   17.453 ± 0.191  ns/op
StrLenTest.panama_strlen_pool       451  avgt    9   23.310 ± 3.391  ns/op


It basically allocates a string and passes it to `strlen`. 
`panama_strlen_alloc` uses a confined arena, whereas `panama_strlen_pool` uses 
a bespoke memory pool to speed up allocation.

This benchmark clearly shows that, for small string sizes, a confined arena is 
very competitive with the bespoke pool. Of course for bigger strings we run out 
of gas, and that's ok.

I think this test is very real-world-shaped, and will probably accurately 
reflect the speedup that many clients will see when doing allocation -> native 
call -> release. (And all this happens w/o any code changes).

-------------

PR Comment: https://git.openjdk.org/jdk/pull/31365#issuecomment-5372311545

Reply via email to