5 ms·
The issue here is that while GC does take some time to perform, it's easy to make the assumption that removing the GC will reclaim the performance lost by the G
by lokedhs 7y ago
The issue here is that while GC does take some time to perform, it's easy to make the assumption that removing the GC will reclaim the performance lost by the GC. This is a fallacy.
We can compare memory strategies like so:
Garbage Collection:
- Allocations: Very fast (often a single instruction)
- Ownership transfer: Free
- Pointer release: Zero
- Post-free: Garbage collection phase (this is where the time is spent)
Reference Counting:
- Allocations (similar to malloc(), requires a binary search at least)
- Ownership transfer: At least one instruction for single-threaded. Multi-threading: Requires a lock.
- Pointer release: Slow. Locking issues, memory housekeeping.
- Post-free: Zero. Good for realtime.
The first point might require some clarification. When you have a compacting garbage collector, the free memory is usually in a single block. This means that allocations merely require a single pointer to be updated. If the pointer hits the end of the free block, you trigger a GC. You don't even have to check for the end of the free block if the subsequent memory page is marked no-access.
One can spend a lot of time measuring the performance impact of all these different steps, and I am not going to try to prove that GC is always faster than refcounting, but at least it should be clear that it's not a simple matter of assuming that having no GC means that you will have no memory management overhead.
- cesarb 7y agoThere's another difference: memory usage. For garbage collection, it's at the minimum only after a full GC (and there's a memory/performance tradeoff, in that using more memory allows doing less GC), while for reference counting, it's always at the minimum. Using more memory for the GC means less memory for other things like the disk cache (which caused a performance problem at work once, which we solved by giving the GC less memory to work with). (Also, "ownership transfer" can be free in reference counting, since the number of references is the same before and after so there's no need to modify the reference count. What isn't free is sharing ownership, since it needs reference count manipulation.)
- lokedhs 7y agoIndeed. Memory usage is a point I never addressed in my comment. When managing large Java applications, that is indeed one of the most important issues that one has to be aware of. Some runtimes tries to be more automatic than others, and there are books worth discussions to be had about that. As for the second paragraph, thank you for clarifying that. I used poor terminology. I should probable have said pointer sharing, or copying. For ownership transfer to be zero cost, the compiler have to be clever enough to figure out that the original reference isn't used after copy. This can be handled by the compiler itself, or be enforced by the language (as is the case with Rust, as far as I understand).
- pjmlp 7y agoThat depends pretty much how the programming language allows for value types, stack allocation, global memory static allocation and off heap access, even in the presence of a GC.
- sfink 7y agoRef counting uses additional memory to store the refcounts. And possibly more for the suspected set of the cycle collector. GC uses additional memory to amortize collections over allocations. (Zero overhead means constantly collecting, using 2x steady state memory size is not uncommon.) Overall, I think you're right, refcounting comes out ahead on memory usage. But it's not completely straightforward to determine.