

0 / 2 embers
0 / 3000 xp
click for more info
Complete a lesson to start your streak
click for more info
Difficulty: 5
click for more info
Not enough gems
Cost: 6 gems
1: Handling Cycles
incomplete
2: Pros and Cons
incomplete
3: Stack Frames
incomplete
4: Tracking Objects
incomplete
5: Free
incomplete
6: Frame References
incomplete
7: Mark and Sweep
incomplete
8: Mark
incomplete
9: Trace
incomplete
10: Sweep
incomplete
Back
ctrl+,
Next
ctrl+.
This lesson's interactive features are locked, please to keep using them
We built a simple reference counting garbage collector. It can handle:
INT and FLOATSTRINGVECTOR3ARRAYHowever, there's one little problem with our implementation. Take a look at this code:
snek_object_t *first = new_snek_array(1);
snek_object_t *second = new_snek_array(1);
// refcounts: first = 1, second = 1
snek_array_set(first, 0, second);
// refcounts: first = 1, second = 2
refcount_dec(first);
// refcounts: first = 0, second = 1
refcount_dec(second);
// refcounts: first = 0, second = 0
// all free!
We create a first array, and shove the second array inside of it. Everything here works as expected. The trouble arises when we introduce a cycle: for example, first contains second, but second also contains first...
Run the code in its current state. Notice that the assertions in main.c fail. Even though we decremented both the first and second arrays' refcounts, neither was freed: the refcounts are not 0!
Fix the assertions to pass by updating them to match the sad reality of our current implementation.
You'll still see a memory leak warning after you get the tests passing. That's expected! We're proving that refcounting is broken for cycles, and we'll fix it in upcoming lessons.
The reason both refcounts are stuck at 1 after being decremented is that, when first has its refcount decremented, it already has 2. So it only drops to 1, which does not trigger a "free" of the second array:
void refcount_dec(snek_object_t *obj) {
if (obj == NULL) {
return;
}
obj->refcount--;
if (obj->refcount == 0) {
// this doesn't happen when refcount is 1
return refcount_free(obj);
}
return;
}
And because second still has 2 refcounts, it also only drops to 1, which fails to trigger a "free" of the first array.
In other words, we have a cycle, and our simple reference counting garbage collector can't handle it.