Reference counting is an incomplete solution it itself - it leaks circular references, and on multithreaded programs it is quite slow. Though some optimizations could be done statically to improve on that.
All in all, a mark-and-sweep will likely be faster (in throughput at least) let alone a good GC.
Circular references wouldn’t matter.
The programmer would still decide when to free the object, not some automatic system. Manual memory management, with Ref Counting just to add some additional Use After Free checks.
For every allocation, also store on the heap a "reference object", that keeps track of this new reference.
Reference track: every time ptr is copied or overwritten, create and delete reference objects as needed.If ptr is freed, visit every reference and call *referenceAddr = NULL, turning all of these references into NULL pointers.