Fix FFM plugin OOM: scope native allocations to per-call Arena.ofConfined()#630
Fix FFM plugin OOM: scope native allocations to per-call Arena.ofConfined()#630
Conversation
…obal Arena.ofAuto() The root cause was that Pi4JNativeContext.ARENA = Arena.ofAuto() is a static field. Every MemorySegment allocated via context.allocate*() was associated with this arena, which lives for the JVM lifetime, so segments were never freed. Memory grew by ~50MB per 250k I2C calls, eventually causing OOM. The fix: each native method call in IoctlNative, SMBusNative, FileDescriptorNative, PermissionNative, and PollNative now creates a fresh Arena.ofConfined() with try-with-resources, scoping all per-call allocations (errno capture buffers, data buffers) to the duration of the call. Pi4JNativeContext.ARENA is retained only for long-lived library lookups in SMBusContext (SymbolLookup.libraryLookup). CAPTURED_STATE_LAYOUT is exposed as public static so Native classes can allocate it in their per-call arenas. The allocateCapturedState() instance method is marked @deprecated. Agent-Logs-Url: https://github.com/Pi4J/pi4j/sessions/0934a648-dac1-4363-b861-cb93bb2be3dd Co-authored-by: FDelporte <1415873+FDelporte@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Pi4J/pi4j/sessions/0934a648-dac1-4363-b861-cb93bb2be3dd Co-authored-by: FDelporte <1415873+FDelporte@users.noreply.github.com>
|
@copilot what are the performance implications with these changes? |
The change is net-positive for performance. Here's the breakdown: Overhead of
Why performance improves overall:
Relative cost: In short: negligible extra cost per call, meaningful reduction in GC overhead as the program runs longer.
See my reply above to @eitch for the full analysis — in short: negligible per-call overhead (nanosecond arena creation vs. tens–hundreds of microseconds for the actual system call), and the change actually improves overall performance by eliminating the growing GC burden caused by millions of accumulated |
eitch
left a comment
There was a problem hiding this comment.
For me this makes sense. We need to test it, but otherwise i think this is the correct approach.
|
@taartspi are you able to test before we merge? |
Pi4JNativeContext.ARENAwas astatic final Arena.ofAuto()field. EveryMemorySegmentallocated for errno capture and data buffers on each native call accumulated there indefinitely — the arena never became unreachable, so GC could never reclaim those segments. Profiler confirmedjdk.internal.foreign.SegmentFactories$1growing from 97k instances (4.7 MB) → 7.4M instances (357 MB) over 750k I2C iterations.Changes
Pi4JNativeContext— ClarifiesARENAis reserved for long-lived library lookups only (e.g.SymbolLookup.libraryLookupinSMBusContext). ExposesCAPTURED_STATE_LAYOUTaspublic static finalfor use in per-call arenas. MarksallocateCapturedState()@Deprecated(since="4.1.0", forRemoval=true).IoctlNative,SMBusNative,FileDescriptorNative,PermissionNative,PollNative— All per-call allocations (errno capture, data buffers) now useArena.ofConfined()in a try-with-resources block, so native memory is freed deterministically at the end of each call.The
contextfield is retained in each Native class solely to trigger*Contextclass loading (and thus staticMethodHandleinitialisation) at construction time, preserving existing error-reporting behaviour.Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
checkstyle.org/opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/tools/linux64/java/bin/java /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/tools/linux64/java/bin/java -jar /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/xml/tools/xml-extractor.jar --fileList=/tmp/codeql-scratch-850ba6d10baafc1e/dbs/java/working/files-to-index16705773135012226395.list --sourceArchiveDir=/tmp/codeql-scratch-850ba6d10baafc1e/dbs/java/src --outputDir=/tmp/codeql-scratch-850ba6d10baafc1e/dbs/java/trap/java(dns block)If you need me to access, download, or install something from one of these locations, you can either: