Lakukan Jalanmu Sendiri. Bagian kedua. Tumpukan



Kami melanjutkan seri artikel kami tentang pengumpul sampah D. Ini adalah bagian kedua dari artikel tentang alokasi memori di luar GC. Bagian pertama berbicara tentang mengalokasikan memori pada stack. Sekarang kita akan melihat alokasi memori dari heap.



Meskipun ini hanya posting keempat dalam seri ini, ini adalah posting ketiga di mana saya berbicara tentang cara menghindari penggunaan GC. Jangan salah: Saya tidak mencoba untuk menakut-nakuti programmer dari pengumpul sampah D. Justru sebaliknya. Memahami kapan dan bagaimana tidak menggunakan GC sangat penting untuk menggunakannya secara efektif.



Sekali lagi, saya akan mengatakan bahwa untuk pengumpulan sampah yang efisien, Anda perlu mengurangi beban pada GC. Seperti yang disebutkan dalam artikel pertama dan selanjutnya dari seri ini, ini tidak berarti bahwa artikel tersebut harus sepenuhnya ditinggalkan. Ini berarti Anda harus berhati-hati tentang seberapa banyak dan seberapa sering mengalokasikan memori melalui GC. Semakin sedikit alokasi memori, semakin sedikit tempat yang tersisa untuk memulai pengumpulan sampah. Semakin sedikit memori di heap pengumpul sampah, semakin sedikit memori yang perlu dipindai.



Tidak mungkin untuk secara akurat dan komprehensif menentukan di aplikasi mana dampak GC akan terlihat dan mana yang tidak - itu sangat tergantung pada program tertentu. Namun kami dapat dengan aman mengatakan bahwa di sebagian besar aplikasi tidak perlu menonaktifkan GC untuk sementara atau seluruhnya, tetapi jika masih diperlukan, penting untuk mengetahui cara melakukannya tanpa GC. Solusi yang jelas adalah mengalokasikan memori pada stack, tetapi D juga memungkinkan memori dialokasikan pada heap biasa, melewati GC.



Xi yang ada di mana-mana



, C . , , - API C. , C ABI, - , . D β€” . , D C.



core.stdc β€” D, C. D, C. , .



import core.stdc.stdio : puts;
void main() 
{
    puts("Hello C standard library.");
}


, D, , C extern(C), , D as a Better C [], -betterC. , . D C , extern(C) . puts core.stdc.stdio β€” , , .



malloc



D C, , malloc, calloc, realloc free. , core.stdc.stdlib. D GC .



import core.stdc.stdlib;
void main() 
{
    enum totalInts = 10;

    //    10   int.
    int* intPtr = cast(int*)malloc(int.sizeof * totalInts);

    // assert(0) ( assert(false))     ,  
    //   assert ,       
    //   malloc.
    if(!intPtr) assert(0, "Out of memory!");

    //      .     
    // ,     ,  
    //  .

    scope(exit) free(intPtr);

    //    ,     
    //  +.
    int[] intArray = intPtr[0 .. totalInts];
}


GC, D . T, GC, T.init β€” int 0. D, . malloc calloc, . , float.init β€” float.nan, 0.0f. .



, , malloc free . :



import core.stdc.stdlib;

//    ,      .
void[] allocate(size_t size)
{
    //  malloc(0)    (  null  - ),     ,    .
    assert(size != 0);

    void* ptr = malloc(size);
    if(!ptr) assert(0, "Out of memory!");

    //    ,      
    //  .
    return ptr[0 .. size];
}

T[] allocArray(T)(size_t count) 
{ 
    // ,      !
    return cast(T[])allocate(T.sizeof * count); 
}

//   deallocate  
void deallocate(void* ptr)
{   
    // free handles null pointers fine.
    free(ptr);
}

void deallocate(void[] mem) 
{ 
    deallocate(mem.ptr); 
}

void main() {
    import std.stdio : writeln;
    int[] ints = allocArray!int(10);
    scope(exit) deallocate(ints);

    foreach(i; 0 .. 10) {
        ints[i] = i;
    }

    foreach(i; ints[]) {
        writeln(i);
    }
}


allocate void[] void*, length. , , allocate , allocArray , , allocate , . , C , β€” , , . calloc realloc, , C.



, (, allocArray) -betterC, . D.



, -



, GC, , . , ~= ~, , GC. ( ). . , , GC.



import core.stdc.stdlib : malloc;
import std.stdio : writeln;

void main()
{
    int[] ints = (cast(int*)malloc(int.sizeof * 10))[0 .. 10];
    writeln("Capacity: ", ints.capacity);

    //      
    int* ptr = ints.ptr;
    ints ~= 22;
    writeln(ptr == ints.ptr);
}


:



Capacity: 0
false


0 , . , GC, , . , , . GC , , . , ints GC, , (. D slices ).



, , , - , malloc .



:



void leaker(ref int[] arr)
{
    ...
    arr ~= 10;
    ...
}

void cleaner(int[] arr)
{
    ...
    arr ~= 10;
    ...
}


, β€” , , . , (, length ptr) . β€” .



leaker , C, GC. : , free ptr ( , GC, C), . cleaner . , , . GC, ptr .



, . cleaner , . , , , @nogc. , , malloc, free, , .



Array std.container.array: GC, , .



API



C β€” . malloc, . , . API: , Win32 HeapAlloc ( core.sys.windows.windows). , D , GC.





, . . . .



, int.



struct Point { int x, y; }
Point* onePoint = cast(Point*)malloc(Point.sizeof);
Point* tenPoints = cast(Point*)malloc(Point.sizeof * 10);


, . malloc D. , Phobos , .



std.conv.emplace , void[], , . , emplace malloc, allocate :



struct Vertex4f 
{ 
    float x, y, z, w; 
    this(float x, float y, float z, float w = 1.0f)
    {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }
}

void main()
{
    import core.stdc.stdlib : malloc;
    import std.conv : emplace;
    import std.stdio : writeln;

    Vertex4f* temp1 = cast(Vertex4f*)malloc(Vertex4f.sizeof);
    Vertex4f* vert1 = emplace(temp1, 4.0f, 3.0f, 2.0f); 
    writeln(*vert1);

    void[] temp2 = allocate(Vertex4f.sizeof);
    Vertex4f* vert2 = emplace!Vertex4f(temp2, 10.0f, 9.0f, 8.0f);
    writeln(*vert2);
}


emplace . , D . , Vertex4f:



struct Vertex4f 
{
    // x, y  z   float.nan
    float x, y, z;

    // w   1.0f
    float w = 1.0f;
}

void main()
{
    import core.stdc.stdlib : malloc;
    import std.conv : emplace;
    import std.stdio : writeln;

    Vertex4f vert1, vert2 = Vertex4f(4.0f, 3.0f, 2.0f);
    writeln(vert1);
    writeln(vert2);    

    auto vert3 = emplace!Vertex4f(allocate(Vertex4f.sizeof));
    auto vert4 = emplace!Vertex4f(allocate(Vertex4f.sizeof), 4.0f, 3.0f, 2.0f);
    writeln(*vert3);
    writeln(*vert4);
}


:



Vertex4f(nan, nan, nan, 1)
Vertex4f(4, 3, 2, 1)
Vertex4f(nan, nan, nan, 1)
Vertex4f(4, 3, 2, 1)


, emplace , β€” . int float. , , . , emplace , .



std.experimental.allocator



. , - , std.experimental.allocator D. API, , , (Design by Introspection), , , . Mallocator GCAllocator , , - . β€” emsi-containers.



GC



GC , D, GC, , GC. , GC. , malloc , new.



GC GC.addRange.



import core.memory;
enum size = int.sizeof * 10;
void* p1 = malloc(size);
GC.addRange(p1, size);

void[] p2 = allocate!int(10);
GC.addRange(p2.ptr, p2.length);


, GC.removeRange, . . free , . , .



GC , , , . . , , . GC , . addRange . , GC, addRange .





addRange. C , .



struct Item { SomeClass foo; }
auto items = (cast(Item*)malloc(Item.sizeof * 10))[0 .. 10];
GC.addRange(items.ptr, items.length);


GC 10 . length . , β€” void[] ( , byte ubyte). :



GC.addRange(items.ptr, items.length * Item.sizeof);


API , , void[].



void addRange(void[] mem) 
{
    import core.memory;
    GC.addRange(mem.ptr, mem.length);
}


addRange(items) . void[] , mem.length , items.length * Item.sizeof.



GC



Artikel ini telah membahas dasar-dasar tentang cara menggunakan heap tanpa menggunakan GC. Selain kelas, ada celah lain yang tersisa dalam cerita kita: apa yang harus dilakukan dengan destruktor. Saya akan menyimpan topik ini untuk artikel berikutnya, yang akan sangat sesuai. Inilah yang direncanakan untuk GC berikutnya dalam seri ini. Tetap berhubungan!



Terima kasih kepada Walter Bright, Guillaume Piolat, Adam D. Ruppe, dan Steven Schveighoffer atas bantuan mereka yang tak ternilai dalam mempersiapkan artikel ini.



Daripada melanjutkan
, . , , . API core.memory.GC inFinalizer , , .



All Articles