快速生活



在有关GC的系列文章的第一篇中,我介绍了D垃圾收集器和使用它的语言功能。我试图传达的两个关键点:



  1. GC仅在您请求内存分配时运行与普遍的误解相反,D语言GC无法在游戏循环中间简单地暂停和暂停Minecraft克隆。它仅在您请求通过它的内存时才运行,并且仅在需要时运行。



  2. 简单的C和C ++样式的内存分配策略可以减少GC的负担不要在循环内分配内存-相反,请尽可能多地预分配资源或使用堆栈。最小化通过GC分配的内存总数。这些策略因#1而起作用。开发人员可以通过智能地使用GC管理的堆分配来指示何时运行垃圾回收。





从第2点开始的策略对于程序员自己编写的代码而言是不错的选择,但是对于第三方库而言,它们并不是特别有用。在这些情况下,使用D语言及其运行时的机制,可以确保在代码的关键点上没有发生内存分配。还有一些命令行选项可以帮助确保GC不会妨碍您。



假设您正在编写D程序,并且出于一个或另一个原因决定完全消除垃圾回收。您有两个明显的解决方案。



贪婪药



第一个解决方案是GC.disable在程序启动时调用通过GC分配内存仍将起作用,但是垃圾收集将停止。所有垃圾回收,包括可能在其他线程上发生的一切。



void main() {
    import core.memory;
    import std.stdio;
    GC.disable;
    writeln("Goodbye, GC!");
}


输出:



Goodbye, GC!


, , GC, , . , , , . , - . :



, , .

, . , - , . GC.enable GC.collect. , C C++.





, @nogc. main, .



@nogc
void main() { ... }


GC. @nogc, main, , . « ».



, GC.disable. .



@nogc
void main() {
    import std.stdio;
    writeln("GC be gone!");
}


:



Error: @nogc function 'D main' cannot call non-@nogc function 'std.stdio.writeln!string.writeln'
(: @nogc- 'D main'    -@nogc– 'std.stdio.writeln!string.writeln')


@nogc , . . @nogc, , , @nogc. , writeln .



:



@nogc 
void main() {
    auto ints = new int[](100);
}


:



Error: cannot use 'new' in @nogc function 'D main'
(:   'new'  @nogc- 'D main')


@nogc- , GC ( ). . , , GC . , , @nogc, .



, @nogc , . , , - ( ). — . :



throw new Exception("Blah");


- , new, @nogc- . , , , - , … , . D , , throw new Exception GC, .



, @nogc- . (. .)

@nogc main — , .



, : @nogc main GC . D . main, — . @nogc, , , GC @nogc-. , @nogc, main, , main , , GC.





. , D, GC . , GC — . , , D: GC . , , .



, , , GC. @nogc / API core.memory.GC . @nogc main, , GC. GC.disable . , GC.enable. , GC (, ), GC.collect.



, , , . API core.memory.GC GC . D.



( !) D --DRT-gcopt=profile:1, . GC, , .



: gcstat.d .



void main() {
    import std.stdio;
    int[] ints;
    foreach(i; 0 .. 20) {
        ints ~= i;
    }
    writeln(ints);
}


GC:



dmd gcstat.d
gcstat --DRT-gcopt=profile:1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
        Number of collections:  1
        Total GC prep time:  0 milliseconds
        Total mark time:  0 milliseconds
        Total sweep time:  0 milliseconds
        Total page recovery time:  0 milliseconds
        Max Pause Time:  0 milliseconds
        Grand total GC time:  0 milliseconds
GC summary:    1 MB,    1 GC    0 ms, Pauses    0 ms <    0 ms


, , , . D GC , ( ) . , , D , GC - ( ).



DMD -vgc, GC — , , ~=.



: inner.d.



void printInts(int[] delegate() dg)
{
    import std.stdio;
    foreach(i; dg()) writeln(i);
} 

void main() {
    int[] ints;
    auto makeInts() {
        foreach(i; 0 .. 20) {
            ints ~= i;
        }
        return ints;
    }

    printInts(&makeInts);
}


makeInts — . , , / ( static, delegate function). .



-vgc:



dmd -vgc inner.d
inner.d(11): vgc: operator ~= may cause GC allocation
inner.d(7): vgc: using closure causes GC allocation

(inner.d(11): vgc:  ~=      GC)
(inner.d(7): vgc:        GC)


, , ints, ( — - delegate). ints makeInts . , - . printInts :



void printInts(scope int[] delegate() dg)


scope , . , dg . , . . , , .





, GC D , Java C#, . , D , Java, . , D. , Java, GC , .



, , , , . D № 2 , @nogc core.memory.GC , . , , , .



, D. , Phobos — D — @nogc. , , .



在以后的文章中,我们将探讨如何在不求助于GC的情况下分配内存,并将其与GC中的内存并行使用,而不是替换@nogc代码中不可用的语言功能等等。



感谢弗拉基米尔·潘捷列夫(Vladimir Panteleev),纪尧姆·皮奥拉特(Guillaume Piolat)和史蒂芬·施维霍夫(Steven Schveighoffer)对本文草案的宝贵反馈。




All Articles