I will start with a small nitpick: ref counting is a GC algorithm.
The answer: tracing GCs have that much better throughput. This is partly due to having to use atomic increments/decrements in multithread environments, which will absolutely kill performance (flush caches, force synchronizations, etc). Another reason which is true even of single-threaded RC is that the work has to be done “on the spot”. Clever compiler tricks can optimize some of it away, but tracing GCs amortize the costs very well (at the cost of more memory usage, there is no free lunch) — if they are moving GCs they can basically use thread-local allocation buffers where allocation can be as cheap as like 4 instructions, and later moving the still used ones out to a different region, and deallocation can be as simple as a flag toggle, now a different region is considered as the used space, the whole of the former one can be considered garbage. Add to it that many parts of this can be made parallel, modern state-of-the-art tracing GCs are really really hard to beat in complex programs.
Also, circular references are extremely common in complex programs, that in itself is a huge reason to have at least some hybrid solution (like python).