Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> It does make me wonder: How practical is it to just use traditional reference counting and then periodically do a mark-and-sweep? I know it's a very different approach than .net was designed for. (Because they deliberately decided that dereferencing an object should have no computational cost.) It's more of a rhetorical question.

This is what CPython does. The trade off is solidly worse allocator performance, however. You also have the reference counting overhead, which is not trivial unless it is deferred.

There is always a connection between the allocator and collector. If you use a compacting collector (which I assumed .NET does), you get bump pointer allocation, which is very fast. However, if you use a non-compacting collector (mark-and-sweep is non-compacting), you would then fallback to a normal free list allocator (aka as "malloc") which has solidly higher overhead. You can see the impact of this (and reference counting) in any benchmark that builds a tree (and therefore is highly contended on allocation). This is also why languages that use free list allocation often have some sort of "arena" library, so they can have high speed bump pointer allocation in hot spots (and then free all that memory at once later on).

BTW, reference counting, malloc/free performance also impact Rust, but given Rust's heavy reliance on the stack it often doesn't impact performance much (aka just doing less allocations). For allocation heavy code, many of us use MiMalloc one of the better malloc/free implementations.



Dotnet does both mark and sweep as well as compaction, depends on what type of GC happens.


In this case, we're discussing a case where mark-and-sweep is used to collect cyclic references, and it's implied that there are no generations. (Because otherwise, purely relying on reference counting means that cyclic references end up leaking unless things like weak references are used.)

IE, the critical difference is that reference counting frees memory immediately; albeit at a higher CPU cost and needing to still perform a mark-and-sweep to clear out cyclic references.


So basically you're trading lowering RAM consumption for higher CPU consumption?

FWIW: When I look at Azure costs, RAM tends to cost more than CPU. So the tradeoffs of using a "slower" memory manager might be justified.


It depends on workload. It is difficult to quantify the trade offs without knowing that.

The problem is in languages like C#/Java almost everything is an allocation, so I don't really think reference counting would work well there. I suspect this is the reason PyPy doesn't use reference counting, it is a big slowdown for CPython. Reference counting really only works well in languages with low allocations. Go mostly gets away with a non-compacting mark-sweep collector because it has low level control that allows many things to sit on the stack (like Rust/C/C++, etc.).


C# is a lot better than Java on this front since they support stack allocated structs




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: