- Don’t Fear the Reaper
- Life in the Fast Lane
- Go Your Own Way. .
- Go Your Own Way. .
我们继续关于D垃圾收集器的系列文章,这是关于GC外部的内存分配的文章的第二部分。第一部分讨论了在堆栈上分配内存。现在我们来看一下堆中的内存分配。
虽然这只是本系列的第四篇文章,但这是我谈论避免使用GC的方法的第三篇文章。没错:我并不是想从D垃圾收集器中吓跑程序员,相反。了解何时以及如何不使用GC是有效使用它的关键。
再一次,我要说的是,为了有效地进行垃圾回收,您需要减少GC的负载。正如该系列的第一篇和后续文章所提到的,这并不意味着应该完全放弃它。这意味着您需要谨慎确定通过GC分配内存的数量和频率。内存分配越少,垃圾收集可以开始的地方就越少。垃圾收集器堆上的内存越少,它需要扫描的内存就越少。
不可能准确,全面地确定GC在哪些应用程序中的影响是显而易见的,而在哪些应用程序中则不会-很大程度上取决于特定的程序。但是我们可以放心地说,在大多数应用程序中,无需暂时或完全禁用GC,但是在仍然需要时,知道如何在不使用GC的情况下很重要。显而易见的解决方案是在堆栈上分配内存,但是D也允许您绕过GC在常规堆上分配内存。
无所不在
不论好坏,C在我们周围无处不在。如今,无论用哪种语言编写的任何程序,都可能在某种程度上访问C API。尽管C规范没有定义标准的ABI,但其特定于平台的怪癖已广为人知。大多数语言都熟练地与之交互。语言D也不例外。实际上,默认情况下,所有D程序都可以访问C标准库。
core.stdc软件包是从标准C库的标头转换而来的D模块的集合,当可执行文件链接到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
本文介绍了如何在不借助GC的情况下使用堆的最基础知识。除了类之外,我们的故事还剩下另一个空白:如何处理析构函数。我将在下一篇文章中保存该主题,因为它非常合适。这是本系列下一个GC的计划。保持联系!
感谢Walter Bright,Guillaume Piolat,Adam D. Ruppe和Steven Schveighoffer在编写本文时提供的宝贵帮助。
, . , , . APIcore.memory.GCinFinalizer, , .