关闭D语言垃圾回收器

25

我是一名C++程序员,考虑在我想要玩耍的个人项目中使用D语言。 我想知道是否有一种方法可以完全禁用垃圾回收器,并且这样做的风险是什么。

我知道我可以通过重写new和delete来使用malloc和free来管理自己的内存,但如果我这样做,我更希望垃圾回收器根本不运行。


3
可以,但这个项目将是一个游戏,游戏有严格的性能要求。我不能让垃圾回收器在帧中间决定运行。 - BigSandwich
17
你认为你能比机器更有效地管理内存吗?是的,我是一名程序员。有些程序员编写了Java垃圾回收器,但这并不是管理内存的唯一方式。 - BigSandwich
9
太好了,除了我不关心字符串性能这一点外。另外,这只是表明D的字符串实现比STLport更好。 D字符串是引用类型这一事实肯定与性能增加更有关系,而不是垃圾回收,尽管垃圾回收使这更容易实现。 - BigSandwich
24
同时,我非常不喜欢你认为你比我更了解我的问题领域的态度。我来这里不是为了进行关于垃圾回收的宗教辩论。我问了一个非常具体的问题。我会感激有更高声望的人能够清除所有这些题外话的内容。 - BigSandwich
10
如果您以60帧每秒的速度运行,1-3毫秒就是6-18%的一帧。当垃圾回收运行时,这是一个明显的峰值。还要记住,不稳定的高帧率可能比稳定的低帧率更糟糕。因此,我要么有帧率峰值,要么只有一个总体较低但看起来更平滑的速率。 - BigSandwich
显示剩余9条评论
4个回答

43
要在D2中关闭GC,请使用以下代码:
import core.memory;

void main(string[] args) {
    GC.disable;
    // Do stuff.
}

如果使用 D1/Phobos:

import std.gc;

void main(char[][] args) {
    std.gc.disable;
    // Do stuff.
}

在 D1/Tango 中:

import tango.core.Memory;

void main(char[][] args) {
    GC.disable;
    // Do stuff.
}
GC可以通过调用GC.enable(D2或D1 / Tango)或std.gc.enable(D1 / Phobos)类似地重新启用。这些可以在程序的任何时刻完成。内部使用计数器,要实际重新启用GC,您必须为每次调用disable()调用一次enable()。
以下是禁用GC时不应该做的事情,因为它们会导致内存泄漏:
1. 不要使用数组追加(~=)运算符或使用.length属性来扩大已经分配的数组。这些依赖于GC释放旧的数组,如果必须重新分配,则程序中可能有别名引用它。 2. 不要使用内置的关联数组。这些唯一的释放方式是通过GC。 3. 大多数Phobos和Tango被设计为假定垃圾收集存在。如果没有GC,则这些库中的函数可能会极大地泄漏内存。 4. 不要禁用GC使用D2闭包。(反正对于游戏来说也不会这么做。)
话虽如此,虽然在D中可以在少数关键代码片段中禁用GC(这些关键片段具有实时约束条件,您可能根本不应该使用任何未明确设计用于实时计算的malloc形式),但它大多数情况下是假定GC存在而设计的。对于您的情况,您仍然可以在所有初始化工作等方面使用GC,并且仅在实际需要进行实时处理的游戏部分禁用它。
另外,GC和手动内存管理可以在D中共存,在实践中,当优化代码时,手动删除一些具有短时间生命周期的大型对象可能会导致显着的加速。这可以类似于C ++使用delete语句完成,即使启用了GC也是安全的。当您没有实时约束时,这为您提供了大多数GC的好处和大多数手动内存管理的性能优势。

25
感谢您回答了我实际提出的问题,而没有假设我不知道自己在做什么。 - BigSandwich
正如Benjamin Thaut所发现的那样:不要使用同类可变参数函数调用。 - ponce
GC.disable并不能禁用垃圾回收器,它只是暂停了垃圾回收。垃圾回收器及其堆仍然存在。 - rustyx

8
如果你想使用malloc和free,请使用std.c.stdlib。GC永远不会触及这些内容。std.gc拥有所有你需要的内存管理工具,包括disable()。
然而,GC并不是一件坏事情。大多数D语言库都会在代码中某个地方没有显式删除内存,所以将其关闭并不能使你成为英雄,但如果你有一些关键性能要求,那么关闭它也是可以的。
GC让所有事情都更加高效,如数组切片和在参数中创建新对象而不需要调用者在任何地方存储引用。好的代码量很少,而且使用GC后代码变得更小。

有一些语言特性需要垃圾回收,字符串拼接和哈希表就是其中的几个。我不确定它们都是什么。 - he_the_great
关闭垃圾回收器(GC)是有效的情况之一是当您可以证明不会使用比可用内存更多的内存时。 - BCS
我觉得我不会经常进行数组切片或字符串操作。有没有一份依赖于GC的语言特性列表?也许我应该将其保留并避免使用这些特性。 - BigSandwich
@BCS GC 并不仅仅关注使用的RAM数量,因此我不能同意这一点,但它确实提供了一个很好的安全保障。 - Tim Matthews
@BS:我会选择让它保持开启状态,避免需要GC的事情-这似乎比随着时间的推移无意中泄漏少量内存更安全。 - Lawrence Dol

2
我正在阅读关于D语言的内容,发现在D的规范中有一项新特性:

40. 更好的C

-betterC是dmd的一个命令行标志,它限制了编译器对某些运行时特性的支持。值得注意的是,使用betterC编译的D程序或库不会链接Druntime。任何编译时特性都没有受到限制。 https://dlang.org/spec/betterc.html

使用此命令行标志的结果之一是禁用GC和依赖它的语言特性。

40.1 结果

由于没有可用的Druntime,许多D特性将无法工作。例如:

  • 垃圾回收
  • 线程本地存储
  • TypeInfo和ModuleInfo
  • 内置线程(例如core.thread)
  • 动态数组(但不包括切片)和关联数组
  • 异常
  • switch with strings
  • final switch
  • synchronized and core.sync
  • 静态模块构造函数或析构函数
  • Struct析构函数
  • 单元测试(使用-betterC标志进行测试与平常一样)
请参见https://dlang.org/blog/2017/08/23/d-as-a-better-c/

1

垃圾回收器可以被移除并替换为一个简单的malloc/free包装器。


不,实际上是不行的。D语言及其运行库的大部分功能无法正常工作,导致该语言变得无法使用。 - rustyx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接