- Don't Fear the Reaper
- Life in the Fast Lane
- Go Your Own Way. .
- Go Your Own Way. .
In the first of a series on GC, I introduced the D garbage collector and the language features that use it. Two key points that I tried to convey:
GC only runs when you request a memory allocation . Contrary to popular misconception, the D language GC cannot simply pause and pause your Minecraft clone in the middle of a game loop. It only runs when you request memory through it and only when needed.
Simple C and C ++ style memory allocation strategies can reduce the load on the GC . Don't allocate memory inside loops - instead, pre-allocate as many resources as possible or use the stack. Minimize the total number of memory allocations through the GC. These strategies work because of # 1. The developer can dictate when to run garbage collection by intelligently using GC-managed heap allocation.
The strategies from point # 2 are fine for code that the programmer writes himself, but they are not particularly helpful when it comes to third-party libraries. In these cases, using the mechanisms of the D language and its runtime, you can ensure that no memory allocation occurs at critical points in the code. There are also command line options to help make sure GC doesn't get in your way.
Let's imagine that you are writing a D program and for one reason or another decided to eliminate garbage collection entirely. You have two obvious solutions.
Pill for greed
The first solution is to call GC.disable
when the program starts. Allocating memory via GC will still work, but garbage collection will stop. All garbage collection, including what may have happened on other threads.
void main() {
import core.memory;
import std.stdio;
GC.disable;
writeln("Goodbye, GC!");
}
Output:
Goodbye, 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
. , , .
In future articles, we will look at how to allocate memory without resorting to the GC, and use it in parallel with the memory from the GC, than replace the @nogc
language features that are not available in the code, and much more.
Thanks to Vladimir Panteleev, Guillaume Piolat and Steven Schveighoffer for their valuable feedback on the draft of this article.