D 垃圾收集器工作吗?

发布于 2024-12-25 09:04:36 字数 1207 浏览 0 评论 0原文

于是我尝试在Windows上运行这个程序来测试D垃圾收集器是否正常工作。

无论我是否指定 -release-inline-O 等,DMD 2.057 和 2.058 beta 都会给出相同的结果。

代码:

import core.memory, std.stdio;

extern(Windows) int GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);

struct MEMORYSTATUSEX
{
    uint Length, MemoryLoad;
    ulong TotalPhys, AvailPhys, TotalPageFile, AvailPageFile;
    ulong TotalVirtual, AvailVirtual, AvailExtendedVirtual;
}

void testA(size_t count)
{
    size_t[] a;
    foreach (i; 0 .. count)
        a ~= i;
    //delete a;
}

void main()
{
    MEMORYSTATUSEX ms;
    ms.Length = ms.sizeof;

    foreach (i; 0 .. 32)
    {
        testA(16 << 20);
        GlobalMemoryStatusEx(ms);
        stderr.writefln("AvailPhys: %s MiB", ms.AvailPhys >>> 20);
    }
}

输出是:

AvailPhys: 3711 MiB
AvailPhys: 3365 MiB
AvailPhys: 3061 MiB
AvailPhys: 2747 MiB
AvailPhys: 2458 MiB
core.exception.OutOfMemoryError

当我取消注释 delete a; 语句时,输出是

AvailPhys: 3714 MiB
AvailPhys: 3702 MiB
AvailPhys: 3701 MiB
AvailPhys: 3702 MiB
AvailPhys: 3702 MiB
...

所以我想问题很明显...... GC 真的起作用吗?

So I tried to test whether the D garbage collector works properly by running this program on Windows.

DMD 2.057 and 2.058 beta both give the same result, whether or not I specify -release, -inline, -O, etc.

The code:

import core.memory, std.stdio;

extern(Windows) int GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);

struct MEMORYSTATUSEX
{
    uint Length, MemoryLoad;
    ulong TotalPhys, AvailPhys, TotalPageFile, AvailPageFile;
    ulong TotalVirtual, AvailVirtual, AvailExtendedVirtual;
}

void testA(size_t count)
{
    size_t[] a;
    foreach (i; 0 .. count)
        a ~= i;
    //delete a;
}

void main()
{
    MEMORYSTATUSEX ms;
    ms.Length = ms.sizeof;

    foreach (i; 0 .. 32)
    {
        testA(16 << 20);
        GlobalMemoryStatusEx(ms);
        stderr.writefln("AvailPhys: %s MiB", ms.AvailPhys >>> 20);
    }
}

The output was:

AvailPhys: 3711 MiB
AvailPhys: 3365 MiB
AvailPhys: 3061 MiB
AvailPhys: 2747 MiB
AvailPhys: 2458 MiB
core.exception.OutOfMemoryError

When I uncommented the delete a; statement, the output was

AvailPhys: 3714 MiB
AvailPhys: 3702 MiB
AvailPhys: 3701 MiB
AvailPhys: 3702 MiB
AvailPhys: 3702 MiB
...

So I guess the question is obvious... does the GC actually work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

长梦不多时 2025-01-01 09:04:36

这里的问题是假指针。 D 的垃圾收集器是保守的,这意味着它并不总是知道什么是指针,什么不是。有时必须假设如果解释为指针,则指向 GC 分配的内存的位模式就是指针。这主要是大型分配的问题,因为大块是错误指针的更大目标。

每次调用 testA() 时,您都会分配大约 48 MB 的空间。根据我的经验,这几乎足以保证在 32 位系统上会有一个指向该块的错误指针。如果您以 64 位模式(Linux、OSX 和 FreeBSD 支持,但 Windows 尚不支持)编译代码,您可能会获得更好的结果,因为 64 位地址空间要稀疏得多。

就我的 GC 优化而言(我是 Cyber​​Shadow 提到的 David Simcha),有两批。一个已经使用超过 6 个月了,没有造成任何问题。另一个仍在作为拉取请求进行审查,并且尚未出现在主运行时树中。这些或许都不是问题。

短期来看,解决方案是手动释放这些巨大的块。从长远来看,我们需要添加精确扫描,至少对于堆而言。 (精确的堆栈扫描是一个更难的问题。)几年前我编写了一个补丁来执行此操作,但它被拒绝了,因为它依赖于模板和编译时函数评估来为每个数据类型生成指针偏移信息。希望这些信息最终将由编译器直接生成,并且我可以为垃圾收集器重新创建精确的堆扫描补丁。

The problem here is false pointers. D's garbage collector is conservative, meaning it doesn't always know what's a pointer and what isn't. It sometimes has to assume that bit patterns that would point into GC-allocated memory if interpreted as pointers, are pointers. This is mostly a problem for large allocations, since large blocks are a bigger target for false pointers.

You're allocating about 48 MB each time you call testA(). In my experience this is enough to almost guarantee there will be a false pointer into the block on a 32-bit system. You'll probably get better results if you compile your code in 64-bit mode (supported on Linux, OSX and FreeBSD but not Windows yet) since 64-bit address space is much more sparse.

As far as my GC optimizations (I'm the David Simcha that CyberShadow mentions) there were two batches. One's been in for >6 months and hasn't caused any problems. The other is still in review as a pull request and isn't in the main druntime tree yet. These probably aren't the problem.

Short term, the solution is to manually free these huge blocks. Long term, we need to add precise scanning, at least for the heap. (Precise stack scanning is a much harder problem.) I wrote a patch to do this a couple years ago but it was rejected because it relied on templates and compile time function evaluation to generate pointer offset information for each datatype. Hopefully this information will eventually be generated directly by the compiler and I can re-create my precise heap scanning patch for the garbage collector.

感受沵的脚步 2025-01-01 09:04:36

这看起来像是回归 - 它不会发生在 D1 (DMD 1.069) 中。 David Simcha 最近一直在优化 GC,所以这可能与此有关。请提交错误报告。

This looks like a regression - it doesn't happen in D1 (DMD 1.069). David Simcha has been optimizing the GC lately, so it might have something to do with that. Please file a bug report.

提笔落墨 2025-01-01 09:04:36

PS 如果您在 makefile 中将 DFLAGS 设置为 -debug=PRINTF 来重建 Druntime,您将通过控制台获得有关 GC 何时分配/释放的信息。 :)

P.S. If you rebuild Druntime with DFLAGS set to -debug=PRINTF in the makefile, you will get information on when the GC allocates/deallocates via console. :)

骷髅 2025-01-01 09:04:36

它确实有效。当前的实现永远不会向操作系统释放内存。虽然 GC 重用了获取的内存,所以这并不是真正的泄漏。

It does work. Current implementation just never releases memory to operating system. Though GC reuses acquired memory, so it's not a really a leak.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文