Reference counting is basically never sufficient for managing memory due to cycles. If a language has mutation we can essentially create a structure like
| | |
| Head | Tail |
| | |
| | |
I put way too much effort into this lousy diagram
Now that the head is pointed at the tail the counter for the object will never dip to 0, meaning it'll lie around forever. This is a persistant issue for Perl and is the reason for the contortions with
weak_prt in C++.
Also frankly a good GC is orders of magnitude faster than reference counting. Bumping those counters constantly (particularly when you need to ensure thread safety) is actually not free. Clever things with generational/parallel garbage collection can give essentially pause free high performance code!
It's a natural question to wonder why we couldn't just start with malloc and free in Lisp and see where that takes us. In a language with closures, however, using manual memory management is a constant perilous battle. You are constantly in grave danger of closing over something with a slightly different lifetime and having things slowly pear-shaped. This complexity is noticeable in C++ with the fine grained notions of capturing and moving in and out of closures, even this destroys the time honored series of tricks in Lisp for simulating objects and other useful creatures.
TLDR: Garbage collection is actually pretty fast and reference counting is just too naive.