Seq.cache memory retention with captured closures in recursive sequences
0 reputation · 11 Oct 2020, 07:56 UTC
0 reputation · 11 Oct 2020, 07:56 UTC
F# Seq.cache memoizes enumerated elements into an internal buffer, retaining every visited item for the lifetime of the cached sequence object. When the source sequence is produced by a recursive seq expression that captures outer variables in its closure, those captured values remain rooted as long as the cached sequence is reachable.
Determine the precise conditions under which the internal buffer and any captured closure references become eligible for garbage collection when a cached sequence is partially enumerated and then abandoned.
IEnumerable<T> should allow collection, but the interaction with closure capture in recursive let rec sequences is not explicitly documented.Seq.cache immediately release the buffered elements and any captured closure references, or does the cache object itself retain them until finalization?seq expression captures a large array and is wrapped with Seq.cache, does partial enumeration followed by releasing all references to the cached sequence allow the captured array to be collected on .NET 8+ across x64 and ARM64?A thoughtful contribution can make all the difference. Be the first to share one.
Use comments to ask for clarification. Post a solution as an answer.
5,960 reputation · 11 Oct 2020, 16:40 UTC
Disposing one enumerator from a Seq.cache instance stops further source pulls, but it does not clear the internal buffer. Already-enumerated elements and the recursive seq closure that produced them remain rooted by the cache until the cached sequence object and every live enumerator are unreachable. On .NET 8 x64 and ARM64, dropping all references after partial enumeration normally makes a captured array eligible for collection, though debug builds or abandoned enumerators can extend apparent lifetime. Verify with a WeakReference to the captured array after dropping references and forcing a diagnostic GC.Collect; treat retained-buffer behavior as implementation-specific on the target runtime.