走自己的路。第一部分。叠放

GC系列


这是GC系列文章中的第三篇。在第一篇文章中,我介绍了D垃圾收集器和需要它的语言功能,还介绍了有效使用它的简单技术。在第二篇文章中,我展示了语言和库中存在哪些工具会在代码的某些位置上限制GC,以及编译器如何帮助您确定值得做的地方,并且还建议在D中编写程序时首先使用大胆使用GC,同时通过简单的策略将其对性能的影响降到最低,然后调整代码以避免GC,甚至仅在分析器保证的情况下进一步优化其使用。



当垃圾回收器被禁用GC.disable或被function属性禁用时@nogc,仍然需要在某个地方分配内存。而且,即使您最大程度地使用GC,还是建议您尽量减少通过GC分配的内存数量和数量。这意味着在堆栈或常规堆上分配内存。本文将重点讨论前者。堆上的内存分配将是下一篇文章的主题。



在堆栈上分配内存



D中最简单的内存分配策略与C中相同:避免使用堆,并尽可能使用堆栈。如果需要一个数组,并且在编译时知道其大小,请使用静态数组而不是动态数组。结构具有值语义,默认情况下在堆栈上创建,而类具有引用语义,通常在堆上创建;只要有可能,结构应该是首选。编译时的D功能可帮助您实现许多其他无法完成的任务。



静态数组



D中的静态数组要求在编译时知道大小。



// OK
int[10] nums;

// :  x     
int x = 10;
int[x] err;


与动态数组不同,通过文字初始化静态数组不会通过GC分配内存。数组和文字的长度必须匹配,否则编译器将生成错误。



@nogc void main() {
    int[3] nums = [1, 2, 3];
}


, , , .



void printNums(int[] nums) {
    import std.stdio : writeln;
    writeln(nums);
}

void main() {
    int[]  dnums = [0, 1, 2];
    int[3] snums = [0, 1, 2];
    printNums(dnums);
    printNums(snums);
}


-vgc , , — . :



int[] foo() {
    auto nums = [0, 1, 2];

    //  -  nums...

    return nums;
}


nums . , , . , .



, - , , . , . .dup:



int[] foo() {
    int[3] nums = [0, 1, 2];

    //  x —  -   nums
    bool condition = x;

    if(condition) return nums.dup;
    else return [];
}


GC .dup, . , [] null — , ( length) 0, ptr null.





D , . , , : .



struct Foo {
    int x;
    ~this() {
        import std.stdio;
        writefln("#%s says bye!", x);
    }
}
void main() {
    Foo f1 = Foo(1);
    Foo f2 = Foo(2);
    Foo f3 = Foo(3);
}


, :



#3 says bye!
#2 says bye!
#1 says bye!


, , . GC new, GC . , . [std.typecons.scoped](https://dlang.org/phobos/std_typecons.html#.scoped) .



class Foo {
    int x;

    this(int x) { 
        this.x = x; 
    }

    ~this() {
        import std.stdio;
        writefln("#%s says bye!", x);
    }
}
void main() {
    import std.typecons : scoped;
    auto f1 = scoped!Foo(1);
    auto f2 = scoped!Foo(2);
    auto f3 = scoped!Foo(3);
}


, , . core.object.destroy, .



, scoped, destroy @nogc-. , , GC, , @nogc-. , nogc, .



, . (Plain Old Data, POD) , - GUI, , . , , . , , , .



alloca



C D « », alloca. , GC, . , :



import core.stdc.stdlib : alloca;

void main() {
    size_t size = 10;
    void* mem = alloca(size);

    // Slice the memory block
    int[] arr = cast(int[])mem[0 .. size];
}


C, alloca : . , arr . arr.dup.





, Queue, «». D , . Java , , . D , (Design by Introspection). , , , , UFCS, ( ).



DbI . (. .)

Queue . , , . , : - . , , , .



//  `Size`   0     
//    ;  
//    
struct Queue(T, size_t Size = 0) 
{
    //       .
    //   `public`  DbI-   
    // ,   Queue   .
    enum isFixedSize = Size > 0;

    void enqueue(T item) 
    {
        static if(isFixedSize) {
            assert(_itemCount < _items.length);
        }
        else {
            ensureCapacity();
        }
        push(item);
    }

    T dequeue() {
        assert(_itemCount != 0);
        static if(isFixedSize) {
            return pop();
        }
        else {
            auto ret = pop();
            ensurePacked();
            return ret;
        }
    }

    //       
    static if(!isFixedSize) {
        void reserve(size_t capacity) { 
            /*      */ 
        }
    }

private:   
    static if(isFixedSize) {
        T[Size] _items;     
    }
    else T[] _items;
    size_t _head, _tail;
    size_t _itemCount;

    void push(T item) { 
        /*  item,  _head  _tail */
        static if(isFixedSize) { ... }
        else { ... }
    }

    T pop() { 
        /*  item,  _head  _tail */ 
        static if(isFixedSize) { ... }
        else { ... }
    }

    //       
    static if(!isFixedSize) {
        void ensureCapacity() { /*  ,   */ }
        void ensurePacked() { /*  ,   */}
    }
}


:



Queue!Foo qUnbounded;
Queue!(Foo, 128) qBounded;


qBounded . qUnbounded, . , . isFixedSize:



void doSomethingWithQueueInterface(T)(T queue)
{
    static if(T.isFixedSize) { ... }
    else { ... }
}


: __traits(hasMember, T, "reserve"), — : hasMember!T("reserve"). __traits std.traits — DbI; .





GC. , , — GC .



在本系列的下一篇文章中,我们将研究无需通过GC即可在常规堆上分配内存的方法。




All Articles