- Jangan Takut Reaper
- Hidup di jalur cepat
- Pergilah dengan Cara Anda Sendiri. Bagian satu. Tumpukan
- Pergilah dengan Cara Anda Sendiri. Bagian kedua. Tumpukan
Ini adalah seri ketiga dari GC. Pada artikel pertama, saya memperkenalkan pengumpul sampah D dan fitur bahasa yang membutuhkannya, dan juga menyentuh teknik sederhana untuk menggunakannya secara efektif. Pada artikel kedua, saya menunjukkan alat apa dalam bahasa dan perpustakaan yang membatasi GC di tempat-tempat tertentu dalam kode, dan bagaimana kompiler dapat membantu mengidentifikasi tempat-tempat yang layak dilakukan, dan juga merekomendasikan saat menulis program dalam D untuk pertama menggunakan GC dengan aman, sementara meminimalkan dampak kinerjanya dengan strategi sederhana, dan kemudian mengubah kode untuk menghindari GC atau bahkan lebih mengoptimalkan penggunaannya hanya jika profiler menjaminnya.
Ketika pengumpul sampah dinonaktifkan GC.disable
atau dinonaktifkan oleh atribut fungsi @nogc
, memori masih perlu dialokasikan di suatu tempat. Dan bahkan jika Anda menggunakan GC secara maksimal, masih disarankan untuk meminimalkan jumlah dan jumlah alokasi memori melalui GC. Ini berarti mengalokasikan memori pada stack atau pada heap biasa. Artikel ini akan fokus pada yang pertama. Alokasi memori pada heap akan menjadi subjek dari artikel selanjutnya.
Mengalokasikan memori pada tumpukan
Strategi alokasi memori yang paling sederhana di D adalah sama dengan di C: hindari menggunakan tumpukan dan gunakan tumpukan sebanyak mungkin. Jika sebuah array diperlukan dan ukurannya diketahui pada waktu kompilasi, gunakan array statis alih-alih yang dinamis. Struktur memiliki semantik nilai dan dibuat di stack secara default, sedangkan kelas memiliki semantik referensi dan biasanya dibuat di heap; struktur harus lebih disukai bila memungkinkan. Kemampuan kompilasi waktu D membantu Anda mencapai banyak hal yang tidak mungkin terjadi sebaliknya.
Array statis
Array statis dalam D membutuhkan ukuran untuk diketahui pada waktu kompilasi.
// OK
int[10] nums;
// : x
int x = 10;
int[x] err;
Tidak seperti array dinamis, menginisialisasi array statis melalui literal tidak mengalokasikan memori melalui GC. Panjang array dan literal harus cocok, jika tidak kompiler akan menghasilkan kesalahan.
@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 .
Pada artikel selanjutnya dalam seri ini, kita akan melihat cara untuk mengalokasikan memori pada tumpukan reguler tanpa melalui GC.