{"record":{"id":"ac53e6211c1fd20b","repo":"apache/flink","slug":"memorysegment-can-be-freed-only-once","errorCode":null,"errorMessage":"MemorySegment can be freed only once!","messagePattern":"MemorySegment can be freed only once!","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/core/memory/MemorySegment.java","lineNumber":244,"sourceCode":"     * @return <tt>true</tt>, if the memory segment has been freed, <tt>false</tt> otherwise.\n     */\n    @VisibleForTesting\n    public boolean isFreed() {\n        return address > addressLimit;\n    }\n\n    /**\n     * Frees this memory segment.\n     *\n     * <p>After this operation has been called, no further operations are possible on the memory\n     * segment and will fail. The actual memory (heap or off-heap) will only be released after this\n     * memory segment object has become garbage collected.\n     */\n    public void free() {\n        if (isFreedAtomic.getAndSet(true)) {\n            // the segment has already been freed\n            if (checkMultipleFree) {\n                throw new IllegalStateException(\"MemorySegment can be freed only once!\");\n            }\n        } else {\n            // this ensures we can place no more data and trigger\n            // the checks for the freed segment\n            address = addressLimit + 1;\n            offHeapBuffer = null; // to enable GC of unsafe memory\n            if (cleaner != null) {\n                cleaner.run();\n                cleaner = null;\n            }\n        }\n    }\n\n    /**\n     * Checks whether this memory segment is backed by off-heap memory.\n     *\n     * @return <tt>true</tt>, if the memory segment is backed by off-heap memory, <tt>false</tt> if\n     *     it is backed by heap memory.","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/core/memory/MemorySegment.java#L226-L262","documentation":"MemorySegment.free() releases a segment exactly once, guarded by an AtomicBoolean. On a second free() the call is normally a no-op unless the static flag checkMultipleFree (set via MemorySegment.setCheckMultipleFree, primarily used in tests) is enabled, in which case it throws IllegalStateException('MemorySegment can be freed only once!'). free() also invalidates the segment by setting address = addressLimit + 1 so any later data access fails.","triggerScenarios":"Calling free() (or a recycling path that frees) twice on the same MemorySegment: e.g. an owner frees the segment on an error path and cleanup code frees it again, or the same segment is returned to a MemoryPool/network-buffer pool twice.","commonSituations":"Double-recycle bugs in network buffer pools (buffer recycled once on emit and once on failure); memory-manager returnSegment paths executed twice; shared ownership of a segment across components without a single designated owner; unit tests that enable checkMultipleFree surfacing pre-existing double frees.","solutions":["Establish a single owner responsible for freeing/recycling each segment and audit error paths so cleanup never frees a segment the failure path already freed.","Guard every free site with if (!segment.isFreed()) { segment.free(); }.","Trace which component freed first (log isFreed()/owner via segment.getOwner()) and remove the duplicate free call.","Only in tests where a double free is known-benign, wrap with MemorySegment.setCheckMultipleFree(false) — never as a production fix."],"exampleFix":"// before\nsegment.free(); // second call throws when multiple-free checks are on\n\n// after\nif (!segment.isFreed()) {\n    segment.free();\n}","handlingStrategy":"validation","validationCode":"if (!segment.isFreed()) {\n    segment.free();\n}","typeGuard":null,"tryCatchPattern":"try {\n    segment.free();\n} catch (IllegalStateException e) {\n    // only reachable when checkMultipleFree is enabled; fix the duplicate free site, do not swallow\n    throw e;\n}","preventionTips":["Designate exactly one owner per MemorySegment responsible for free()/recycle.","Audit error paths: failure handlers must not free what normal cleanup will also free.","Run unit/integration suites with MemorySegment.setCheckMultipleFree(true) to surface double frees early."],"tags":["memory","lifecycle","flink-core","double-free"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}