Comment #0 by default_357-line — 2023-01-09T13:11:13Z
In my post A GC Memory Usage Experiment https://forum.dlang.org/post/[email protected] , I suggested the existence of a GC leak caused by downsizing data structures. This bug report poses a proof-of-concept for such a leak:
struct S {
S[] parent;
}
void main() {
S parent;
while (true) {
S[] link = [S(null), parent];
link.length = 1;
parent = S(link);
}
}
As can be seen, at any given point almost no memory in this program is actually live: `parent` can only point at an array of the value `[S(null)]`, and all other variables get overwritten on every loop pass.
And yet, this program leaks an unbounded amount of memory. (I recommend running with -m32 to test.)
What's happening is that the program forms a linked list in memory that is dead, but that the GC cannot determine is dead. Because the GC has no type-level understanding of allocated memory, it sees `parent` as a pointer to a linked list of allocations; that the linking element lives in an unreferenced part of the array is outside of its purview.
In theory this could be fixed by being smarter about arrays marking memory regions as alive: a slice need only mark as alive the part of the array it actually points at, which would allow the recursive mark to skip the dead parent reference.
Comment #1 by default_357-line — 2023-01-09T13:20:45Z
Interestingly, this issue cannot be provoked with associative arrays - probably deleting a key zeroes out the associated value.
Comment #2 by ibuclaw — 2023-01-09T19:18:20Z
(In reply to FeepingCreature from comment #0)
> In my post A GC Memory Usage Experiment
> https://forum.dlang.org/post/[email protected] , I
> suggested the existence of a GC leak caused by downsizing data structures.
> This bug report poses a proof-of-concept for such a leak:
>
> struct S {
> S[] parent;
> }
>
> void main() {
> S parent;
> while (true) {
> S[] link = [S(null), parent];
> link.length = 1;
I assume no zeroing is done here because you might have other slices to the data.
auto slice = link[0 .. $];
link.length = 1;
assert(slice[1] == parent);
Comment #3 by default_357-line — 2023-01-10T02:57:13Z
Well, there's lots of ways to avoid this issue. Zeroing, as you say. I'm just putting up this bug to note it's a straightforward issue as it stands. We're used to treating the GC the way we know it works rather than the way it theoretically could work, doing array.dup to cut off dead elements, etc., and at any rate the GC isn't *obligated* to collect anything ever, etc etc. But naively, this is still surprising behavior.
Comment #4 by robert.schadek — 2024-12-13T19:26:37Z