The author tosses the word &quot;performance&quot; around a lot even though it means different things in different environments, especially when it comes to GC.<p>First, the main cost a GC -- any GC -- always has is significantly increased RAM overhead. Whether RAM is cheap (and can be traded off for things like throughput&#x2F;latency or convenience) or not depends a lot on the environment. If RAM is expensive -- a GC is not the answer. If it isn&#x27;t, it may well be the best answer. OTOH, GCs at least potentially have an advantage that&#x27;s very hard to achieve by other means, which is compaction. Everything else is a tradeoff.<p>Second, I think the author jumps the gun when discussing various solutions (ownership, stack allocation, reference counting) without first trying to understand the problem, and the problem is -- first and foremost -- RAM overhead. Whether or not that&#x27;s a serious problem really depends on the environment.<p>Also, before discussing solutions, we need to understand the domain. I think that the domain is as follows. Objects belong in one of four lifecycles:<p>1. Permanent -- objects live for the duration of the program<p>2. Stack -- objects live for the duration of a single function (and the functions it calls, of course)<p>3. Arbitrary -- objects live for an undetermined lifetime<p>4. Transaction -- objects live for a well-defined, time-bounded domain scope, be it a request in a web server, a frame in a game, or a transaction in a DB.<p>Now, in spite of what some may think, very little of a serious program&#x27;s data can live on the stack. Server machines today come with hundreds of GBs of RAM, and if you do the math, only a very, very, small portion of that can be exploited by kernel-thread stacks. Lightweight threads require growable stacks and those stacks are therefore heap objects themselves, with either arbitrary or transaction lifetime. The cost of allocating stack-scoped objects on the heap is quite low, as those are very short-lived objects that don&#x27;t become tenured by the GC, but allocating on the stack is better because we&#x27;d like to avoid triggering young-gen collections, too, of we can help it (and, indeed, Java is getting value types, although  <i>mostly</i> for a different reason[1]).<p>With so much available RAM, the best use an application can make of it is to cache as large a working-set from the DB in RAM. Those objects have an arbitrary lifetime and often require scalable concurrent access (or sharding, but that opens a whole new can of worms). Reference counting is not a good solution here.<p>Finally, transaction-scoped objects are best handled by arenas. GCs, like HotSpot&#x27;s G1 can attempt to automatically determine those transaction-scoped objects, but the results are not as good. This is a tradeoff: a global GCs simplicity, vs. arenas&#x27; utility and low latency.<p>Personally, I think that the solution first depends on whether or not you&#x27;re targeting high-performance in constrained environments (like C&#x2F;C++ and Rust do). If so, a solution like ref-couting is a great one. If not, I see no room whatsoever for ref-counting (what scope do they target, and what advantage do they have over a GC in an unconstrained environment?)<p>In <i>unconstrained</i> environments I think the best approach is as follows:<p>1. Stack allocation (value objects) where possible.<p>2. A global GC for the arbitrary and permanent scope (no need to separate permanent objects as they place a relatively low burden on a GC).<p>3. Arenas for transaction scopes, <i>if</i> you&#x27;re willing to sacrifice some simplicity for latency.<p>I don&#x27;t see absolute necessity for ownership types (and certainly not reference counting) in such <i>unconstrained environments</i>, but they may certainly be useful in statically verifying that heap objects never reference an arena object (or arenas with wider scopes never reference shorter-lived arenas).<p>In <i>constrained environments</i> where manual memory management is required because the GC&#x27;s RAM overhead is onerous, ownership types are invaluable, as is reference counting. You don&#x27;t get compaction, but this may not matter as such applications are usually much more careful with managing their allocations and deallocations.<p>RTSJ -- Java for hard-realtime and safety-critical systems (which is getting a new spec these days) -- makes the simplicity tradeoff, and introduces arenas (scoped memory): <a href="https:&#x2F;&#x2F;www.aicas.com&#x2F;cms&#x2F;en&#x2F;rtsj" rel="nofollow">https:&#x2F;&#x2F;www.aicas.com&#x2F;cms&#x2F;en&#x2F;rtsj</a> It also makes permanent objects explicit, because it can&#x27;t tolerate any additional cost, even if it mostly limited to program startup.<p>Finally, the author mentions both immutability and large objects, but the two are often at odds. Persistent data structures that are popular in immutable-by-default languages like Clojure rely on small object with lots of pointers to <i>reduce</i> memory and performance costs.<p>[1]: Cache friendliness of arrays and other data structures.