Testcase:
-----
import core.memory;
import core.stdc.stdio;
import std.range;
import std.format;
int toMB(ulong size) { return cast(int) (size / 1048576.0 + 0.5); }
void printGCStats()
{
const stats = GC.stats;
const used = toMB(stats.usedSize);
const free = toMB(stats.freeSize);
const total = toMB(stats.usedSize + stats.freeSize);
printf(" GC stats: %dM used, %dM free, %dM total\n", used, free, total);
}
void formatRange()
{
string str = format("%s", iota(100_000_000));
printf(" string size: %dM\n", toMB(str.length));
}
void main()
{
printGCStats();
while (true)
{
puts("Starting");
formatRange();
GC.collect();
printGCStats();
}
}
-----
On Windows, with DMD 2.092:
> dmd -m64 test.d
> test.exe
GC stats: 0M used, 1M free, 1M total
Starting
string size: 943M
GC stats: 1168M used, 1139M free, 2306M total
Starting
string size: 943M
GC stats: 1168M used, 2456M free, 3623M total
Starting
string size: 943M
GC stats: 1168M used, 2456M free, 3623M total
Starting
string size: 943M
GC stats: 1168M used, 2456M free, 3623M total
Starting
string size: 943M
GC stats: 1168M used, 2456M free, 3623M total
...
The expected behavior is that there should be no used GC memory after an iteration - the huge string is built in another function and doesn't escape, and the manual collection should mark all memory as being unused.
Using the precise GC via `test.exe --DRT-gcopt=gc:precise` makes no difference.
No idea whether that's a GC issue (druntime) or std.format doing weird things.
Comment #1 by kinke — 2020-06-27T15:04:38Z
Seems to be GC-specific, as it also happens for a huge linked list, see https://forum.dlang.org/post/[email protected] (yes, even when extracting the actual work and `dll` ref on the stack into a dedicated function).
Comment #2 by robert.schadek — 2024-12-07T13:40:23Z