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
Latest version looks great. Left a few comments/suggestions inline
src/java.base/share/classes/jdk/internal/foreign/ConfinedSegmentPool.java line
97:
> 95: private static final long POOLED_MEMORY_SIZE =
> clampedPowerOfPropertyOr(POOLED_MEMORY_PROPERTY, 6);
> 96:
> 97: private static final int PLATFORM_POOL_COUNT = 4;
Could we make this a property as well?
src/java.base/share/classes/jdk/internal/foreign/ConfinedSegmentPool.java line
120:
> 118: @ForceInline
> 119: static long acquire(Thread thread) {
> 120: assertCurrentThreadInDebugMode(thread);
I believe I suggested this assert, but I was thinking more of just having
something like:
Suggestion:
assert thread == Thread.currentThread();
Which would run when system asserts are enabled, for instance when running
tests.
src/java.base/share/classes/jdk/internal/foreign/ConfinedSegmentPool.java line
253:
> 251: @ForceInline
> 252: private static Thread cacheOwner(Thread thread) {
> 253: return thread.isVirtual() ? JLA.currentCarrierThread() : thread;
Is it safe to access the carrier thread like this? Are we sure a virtual thread
can not be moved to another thread while we are looking at the pools?
src/java.base/share/classes/jdk/internal/foreign/ConfinedSegmentPool.java line
278:
> 276: if (VM.isDirectMemoryPageAligned()) {
> 277: return -1;
> 278: }
I think this if statement warrants a comment.
-------------
PR Review: https://git.openjdk.org/jdk/pull/31365#pullrequestreview-4964363911
PR Review Comment: https://git.openjdk.org/jdk/pull/31365#discussion_r3855713504
PR Review Comment: https://git.openjdk.org/jdk/pull/31365#discussion_r3855727516
PR Review Comment: https://git.openjdk.org/jdk/pull/31365#discussion_r3855805769
PR Review Comment: https://git.openjdk.org/jdk/pull/31365#discussion_r3855824894