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
