On Thu, 18 Jun 2026 14:09:05 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/per-slot 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: one lazily allocated pool per Thread, encoded in
>> `Thread.confinedMemoryPool`.
>> Virtual threads: fixed shared native pool with CAS-protected slots, because
>> per-virtual-thread native pools would not scale.
>>
>> 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.
>>
>> By default, a confined arena has access to 64 bytes of pooled data. The
>> pool size is 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. Nested confined arenas are not supported
>>
>> ## 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 arena
>> with 3,207,623,039 B (3,059.03 MiB) (46.794% of all by...
>
> Per Minborg has updated the pull request incrementally with two additional
> commits since the last revision:
>
> - Remove field directly in Thread and VirtualThread
> - Revert old changes
src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java line 90:
> 88: ConfinedSegmentPool.release(session.owner, poolSp);
> 89: }
> 90: session.resourceList.cleanup();
Is it important for the release of the pool to be ordered before the resource
list cleanup? Why not just a simpler `super.close();` followed by the pool
release here? (Could you add a comment?)
src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java line 90:
> 88: public ArenaImpl asArena() {
> 89: return ArenaImpl.of(this);
> 90: }
I suggest just overriding `asArena` in `ConfinedSession` rather than using the
`instanceof` check in `ArenaImpl::of`
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/31365#discussion_r3436577370
PR Review Comment: https://git.openjdk.org/jdk/pull/31365#discussion_r3436545464