Reference counting has its advantages, but some severe disadvantages:
- Cyclical data structures won't get freed.
- Every pointer copy requires an increment and a corresponding decrement - including when simply passing a reference to a function.
- In a multithreaded app, the incs and decs must be synchronized.
- Exception handlers (finally blocks) must be inserted to handle all the decs so there are no leaks. Contrary to assertions otherwise, there is no such thing as "zero overhead exceptions."
- In order to support slicing and interior pointers, as well as supporting reference counting on arbitrary allocations of non-object data, a separate "wrapper" object must be allocated for each allocation to be ref counted. This essentially doubles the number of allocations needed.
- The wrapper object will mean that all pointers will need to be double-dereferenced to access the data.
- Fixing the compiler to hide all this stuff from the programmer will make it difficult to interface cleanly with C.
- Ref counting can fragment the heap thereby consuming more memory just like the gc can, though the gc typically will consume more memory overall.
- Ref counting does not eliminate latency problems, it just reduces them.
- The proposed C++ shared_ptr<>, which implements ref counting, suffers from all these faults. I haven't seen a heads up benchmark of shared_ptr<> vs mark/sweep, but I wouldn't be surprised if shared_ptr<> turned out to be a significant loser in terms of both performance and memory consumption.
That said, D may in the future optionally support some form of ref counting, as rc is better for managing scarce resources like file handles. Furthermore, if ref counting is a must, Phobos has the std.typecons.RefCounted type which implements it as a library, similar to C++'s shared_ptr<>.