<p>Generally speaking, higher-level programming languages seem to utilize garbage collection.</p>
<p>As a first example, Python <a href="https://devguide.python.org/internals/garbage-collector/" rel="noreferrer">uses reference counting</a> (as well as three-level generational collection that just cleans up container objects.) Another example would be JVM, Java's Virtual Machine, which utilizes <em>five</em> different GC implementations. Specifically, those are:</p>
<ul>
<li>Serial Garbage Collector</li>
<li>Parallel Garbage Collector</li>
<li>CMS (Concurrent Mark and Sweep) Garbage Collector</li>
<li>G1 (Garbage first) Garbage Collector</li>
<li>Z Garbage Collector</li>
</ul>
<p>But I'm clueless about how these all work and which ones provide what benefits.</p>
<p>What are the different approaches to garbage collection, and what are the benefits of each approach?</p>

<garbage-collection>
What different types of garbage collection are there, and what benefits do they provide?
CC BY-SA 4.0
2023-05-20T21:51:58.200Z
@mousetail Quite the contrary! During the first few weeks of a site, it's especially important to avoid broad questions. If you seed the site with broad questions — which so far seems alarmingly to be the case — then you're just reinventing Wikipedia, not creating a site that's likely to attract experts.
Gilles 'SO- stop being evil'
CC BY-SA 4.0
2023-05-21T05:35:37.210Z
@Gilles'SO-stopbeingevil' Yea we've overdone it a bit but in general a site should go from more broad to more specific over time. We've over-saturated the broad side of the spectrum currently though, but are actively working to rectify that issue
mousetail 'he-him'
CC BY-SA 4.0
2023-05-21T10:06:44.890Z
@mousetail “a site should go from more broad to more specific over time” no, absolutely not. Broad questions will come naturally. Seeding the site with broad question during the early beta is a sure fire way to make it boring because it's just rehashing content that you can find elsewhere.
Gilles 'SO- stop being evil'
CC BY-SA 4.0
2023-05-21T10:38:52.257Z
@Gilles'SO-stopbeingevil' I'm trying to prevent the issue that SO has where old the canonical close targets don't actually ask the question they became canonical for, but instead ask for some very specific sub-issue. It's better to purposefully ask these broad questions then let them arise organically when you have no choice about the content and good answers may be posed on terrible questions
mousetail 'he-him'
CC BY-SA 4.0
2023-05-16T17:27:18.897Z
*Please* let me know if this is far too broad for the site's scope!
Spevacus
CC BY-SA 4.0
2023-05-16T17:28:52.670Z
too broad doesn't really matter that much when a site is new, later it will become more important
mousetail 'he-him'
<h1>Mark-and-sweep collectors</h1>
<p>Simple mark-and-sweep collectors attach a separate boolean to every allocated &quot;object&quot;, and somehow keep track of every living object (you can imagine this as a big list of pointers, but they're usually slightly cleverer — it doesn't matter for this description). They also have an idea of what &quot;roots&quot; are accessible directly - these are things like global variables, local scopes that are currently running, whatever is applicable to the language.</p>
<p>The collection phase has four steps:</p>
<ol>
<li><p><strong>Stop the world</strong>: prevent anything else in the program from happening while the collection runs. Usually the collector runs right before an object would be allocated, and just holds up the rest of the program until it returns, which could be a while.</p>
</li>
<li><p>Go through <em>every</em> living object and <strong>set the marker boolean to false</strong>. Commonly this property is actually established at creation and reset at the end of the collection, but I'll list it here explicitly.</p>
</li>
<li><p>Starting at each root</p>
<ol>
<li>Set its marker to true.</li>
<li>For each reference contained in this object (in a field, the surrounding scope, ...), perform step 3 with that object as the root. If the marker is already set, just move on to the next object.</li>
</ol>
<p>Once every root has been processed, the &quot;mark&quot; phase is complete.</p>
</li>
<li><p>Go through every object again, and <strong>release any object whose marker isn't set</strong>. This object wasn't reachable from any of the roots, so we know the program can never access it again.</p>
<p>If the marker was set, unset it ready for the next collection. This is the &quot;sweep&quot; phase.</p>
</li>
</ol>
<p>This is the model for a single-threaded collector with no optimisations. Slightly smarter mark-and-sweep collectors have multiple levels of marking, move objects around, and so on, but the core approach works like this.</p>
<h2>Tri-colour mark and sweep</h2>
<p>In particular, a tri-colour system is a smarter mark-and-sweep system with three levels of marker instead of two: grey objects are known to be reachable from the roots, black objects are known to be reachable <em>and</em> have had all of their children marked grey, and white objects haven't been examined yet. These markers can be updated piecewise: pick a grey object, mark it black, and mark every white object it references as grey.</p>
<p>When there are no grey objects left, all the white ones are objects that couldn't be reached from the root, so they are freed and the process starts again.</p>
<p>This process can happen in multiple steps, each only moving a few objects from grey to black, so there doesn't need to be one big pause to look at everything. Every object is inspected at some point, however.</p>
<h2>Advantages and Disadvantages</h2>
<p>There are two main advantages of this approach:</p>
<ol>
<li>It is guaranteed to free every unreachable object, even if it's within a reference cycle.</li>
<li>It's fairly straightforward to implement.</li>
</ol>
<p>For these reasons it's not uncommon for a mark-and-sweep phase to be included <em>within</em> more advanced collectors. The disadvantages tend to outweigh the benefits as the main strategy, however:</p>
<ol>
<li><p>It's slow. It has to inspect every object in the system multiple times, every time.</p>
<p>Tri-colour marking spreads the work out over time, but it still does it. In some situations, like user interfaces, one big pause is unacceptable but many smaller ones are ok, but in others this doesn't make much difference.</p>
</li>
<li><p>It relies on stopping every thread until the collection is done.</p>
</li>
<li><p>It has poor memory locality because it is always jumping between objects.</p>
</li>
<li><p>The naïve releasing strategy I described tends to fragment memory quite badly.</p>
</li>
</ol>
<p>When there is a relatively small number of objects (such as within a nursery of a generational collector), or where the completeness properties are desired (such as for occasionally reclaiming reference cycles left by a more efficient reference-counting collector) it can be useful. For cases where raw performance isn't very important but catching escaped reference cycles is, this is a sensible cheap-to-implement strategy.</p>

CC BY-SA 4.0
2023-05-17T00:07:47.877Z
2023-05-17T00:07:47.877Z
Michael Homer
<h1>Generational Garbage Collection</h1>
<p>Generational garbage collection is a great tool in a language where small amounts of data are created and destroyed constantly, such as in a purely functional language (where &quot;slightly modified&quot; versions of data are constantly being constructed). The idea is simple: Most data will either die young or live a very long time, so if we optimize for freeing <em>recently allocated</em> memory, that will handle the most common cases. The popular Haskell compiler GHC uses one of these on the backend.</p>
<p>In its simplest form, a generational garbage collector has two generation: short-term storage and long-term storage, with the former being a much smaller data store. All new data is allocated in short-term storage. When short-term storage gets full, we run a mark-and-sweep garbage collection pass on only that small collection of data, freeing anything that we deem unreachable. If, after that pass, short-term storage is still occupied above some threshold, then we move all of that data to long-term storage and start a new, empty short-term storage region (in practice, this &quot;move&quot; is trivial and simply involves marking that buffer as long-term and creating a new one elsewhere). We monitor the total occupied space of long-term storage and run a full mark-and-sweep pass on <em>all</em> of our memory if it gets too full, but the idea is that this happens very infrequently.</p>
<p>The fundamental invariant of generational garbage collection is: Data can never point to data in a younger generation.</p>
<p>In a language with immutable data such as Haskell, this is a very simple, elegant algorithm, and short-term sweeps are fast. Since data is immutable<sup>[1]</sup>, it's impossible for data to point to anything allocated after it. That is, nothing in long-term storage can ever point to short-term storage, so when we do a short-term sweep, we don't even have to think about long-term storage. Data can be cyclic, so data in short-term can point to other short-term data, if both were allocated at the same time. This can arise when <a href="https://wiki.haskell.org/Tying_the_Knot" rel="noreferrer">tying the knot</a>, for instance. But cycles can never span generations.</p>
<p>In a language with mutable data, this requires some special handling when a pointer is reassigned. If we take some data in long-term storage and reassign it so that it points to data in short-term storage, we either have to make a global note of that for the mark-and-sweep algorithm, or we have to regress the long-term data back into short-term storage.</p>
<hr />
<p><sup>[1]</sup> With the exception of <code>IORef</code>, which already has special handling.</p>

CC BY-SA 4.0
2023-05-16T20:04:38.663Z
2023-05-16T20:04:38.663Z
Silvio Mayolo
<h1>Pure ARC</h1>
<p>This is what Swift uses, and many (but not all) Objective-C projects.</p>
<p>ARC stands for “automated reference counting”. It automatically keeps track of how many references there are to an object, and deallocates the memory when that number hits 0.</p>
<h2>Advantages</h2>
<ul>
<li>Fast: Because there’s no need for a garbage collector to perform tracing or seek out unreferenced objects, there is significantly less processor and even memory overhead. When Apple introduced GC to Objective-C, they never implemented it for iOS, citing that ARC was more power-friendly on mobile devices.</li>
<li>Simple: It’s very straightforward to implement reference counting from scratch.</li>
</ul>
<h2>Disadvantages</h2>
<ul>
<li>Reference cycles: ARC by itself will never deallocate two objects that reference each other. Thus, you need the concept of a <em>weak reference</em>: a reference that doesn’t affect the refcount. Knowing when to use weak references puts more burden on the programmer and can be confusing to beginners.</li>
<li>Frequency: If two objects pass the same reference back and forth, this can cause a lot of “ARC traffic” for something that won’t be deallocated. Not to say that no other GC system does this, but ime a lot of real-time Swift code goes out of its way to minimize changes to the refcount.</li>
<li>Depth: If you delete the root of a very deep object tree, the chain of deallocations can blow the call stack. In some cases it’s possible to avoid this by e.g. scheduling the deallocation to occur on the next cycle of the run loop, but not all ARC languages give you control over this.</li>
</ul>

CC BY-SA 4.0
2023-05-16T17:54:21.753Z
2023-05-17T15:27:58.640Z
2023-05-17T15:27:58.640Z
Bbrk24
CC BY-SA 4.0
2023-09-28T16:13:54.993Z
Another variation on weak references is to have counters for "how many objects are interested in an object" and "how many objects hold references to an object", recognizing that many objects will need to hold references to objects that they're not particularly interested in, as long as anything else is interested in them.  For example, if an object exists to record the last time an entity performed an action satisfying some criterion, the entity would need to hold a reference to it so it can let it know when potentially interesting actions occur, but would have no interest in the object...
supercat
CC BY-SA 4.0
2023-09-28T16:17:08.253Z
...once nothing else was interested in knowing about the monitored actions.  Cycles among references that are interested in each other are less common than situations where cyclic paths exist that would involve traversing different kinds of references.
supercat
CC BY-SA 4.0
2023-09-28T17:11:46.273Z
@supercat That just sounds like a different policy on when to use `weak` (Swift keeps separate strong and weak refcounts).
Bbrk24
CC BY-SA 4.0
2023-09-28T18:45:07.410Z
@Bbrk24: In the approach I describe, the "weak-ish" references could still be used to identify objects even after it's discovered that that nobody is "interested" in them.  I don't know about whether that's possible in Switft; in .NET such semantics could be achieved by having a long weak reference to an object and a ConditionalWeakTable which ties the target of that reference to a finalizable object which is unreferenced anywhere else, and contains a reference to the long weak reference target.
supercat
CC BY-SA 4.0
2023-05-16T18:00:03.757Z
PHP uses reference counting but it's OK because it's intended for short-running scripts
mousetail 'he-him'
CC BY-SA 4.0
2023-05-16T18:30:40.660Z
@mousetail Some garbage collectors use refcount + something else. That’s why I specified “_pure_ ARC” in the title, to distinguish it from RC + other.
Bbrk24
CC BY-SA 4.0
2023-05-16T18:33:32.910Z
PHP has pure refcount. I don't know any garbage collector that does not have refcount, it's the basis for pretty much every other system
mousetail 'he-him'
CC BY-SA 4.0
2023-05-16T18:34:56.350Z
I don’t know much about how GC works for a lot of modern languages. I’m sure there’s some weird stuff out there I’d never think of.
Bbrk24
CC BY-SA 4.0
2023-05-17T03:45:28.910Z
[PHP does have a GC which collects cycles](https://www.php.net/manual/en/features.gc.collecting-cycles.php).
kaya3
<p>Some people argue (to death ) Reference Counting is <em>not</em> a Garbage Collection technique, but the <a href="http://gchandbook.org/" rel="nofollow noreferrer">GC Handbook</a> lists it, discusses and compares, and that's <strong>the</strong> definitive reference. </p>
<p><a href="https://dl.acm.org/doi/10.1145/1028976.1028982" rel="nofollow noreferrer">A unified theory of garbage collection</a> paper goes a bit further and shows the duality between (&quot;pure&quot;) tracing GC and RC, while collectors used in practice implement some &quot;hybridisation&quot; of both.</p>
<p>So we can establish the first dimension of the GC (design) space: on one side tracing GCs that traverse <em>live</em> objects, and opposite to them RC that traverses <em>dead</em> objects (imaging you have a huge tree with no links to nodes except from a parent node, and now the root of the tree becomes unreachable — reference counter goes 0, you deallocate the node and decrease counters of all it's children, which go 0 too, so you now have to deallocate them too, and so on until you traverse and deallocate all the now-garbage; while tracing GC would evacuate live objects and deallocate the whole region of memory regardless of which objects are dead inside it and how they are interconnected).</p>
<p>Another dimension is concurrency and parallelism, which are different notions. A <em>parallel</em> GC can use several cores at the same time to mark live objects and/or move/copy and/or free/compact memory. While <em>concurrent</em> GC can do that <em>while program does it work</em> and allocates new objects/forgets about the old ones. Concurrent GC might do that in a single thread or in a parallel fashion. There's also a notion of <em>incrementality</em>: incremental GC can do some work, then stop, then resume from the same spot. Some people use &quot;concurrent&quot; and &quot;incremental&quot; as synonyms — obviously, if you have an incremental GC you can turn it into concurrent by running preemptively with mutators.</p>
<p>But these are kinda <em>internal</em> GC properties, from an external point of view, the main characteristics of a GC are throughput, latency and space (overhead).</p>
<p>Throughput (as per usual) is how much garbage a collector can free per (sizeable) unit of time. It's very important if an application works in bursts allocating lots of temporary objects that need to be quickly freed before the next burst. Often Stop-the-World (STW) <em>parallel</em> collectors provide highest throughput.</p>
<p>Most usual throughput conflicts with <em>latency</em> i.e. how long a mutator thread have to wait for the GC to do its work before mutator can continue doing something useful. Modern GCs like the Java's Z and Shenandoha exhibit extremely low latencies.</p>
<p>Finally most GCs (RCs might show a counterexample) incur some memory overhead. The most obvious case is two-space copying collector that requires twice as much memory as the application otherwise would use.</p>
<p>And that's just the general very superficial picture, while there are <strong>lots</strong> of implementation details and programming language and application and workload specifics.</p>
<p>That said, I'd claim that well-designed and well-optimized tracing GC is overall more performant and beneficial that (general) reference counting. But very hard to implement. Still &quot;serious languages&quot; like Kotlin Native and AssemblyScript went from an RC to a tracing GC (somewhat recently).</p>

CC BY-SA 4.0
2023-05-17T14:59:59.157Z
2023-05-17T14:59:59.157Z
Alex Chichigin
# What different types of garbage collection are there, and what benefits do they provide?
Posted by: username_0 on 2023-05-16
Generally speaking, higher-level programming languages seem to utilize garbage collection.

As a first example, Python [uses reference counting](https://devguide.python.org/internals/garbage-collector/) (as well as three-level generational collection that just cleans up container objects.) Another example would be JVM, Java's Virtual Machine, which utilizes *five* different GC implementations. Specifically, those are:

* Serial Garbage Collector
* Parallel Garbage Collector
* CMS (Concurrent Mark and Sweep) Garbage Collector
* G1 (Garbage first) Garbage Collector
* Z Garbage Collector

But I'm clueless about how these all work and which ones provide what benefits.

What are the different approaches to garbage collection, and what are the benefits of each approach?
**Tags:** garbage-collection
> **Comments:**
> **username_0**: *Please* let me know if this is far too broad for the site's scope!  
> **username_2**: too broad doesn't really matter that much when a site is new, later it will become more important  
> **username_1**: @mousetail Quite the contrary! During the first few weeks of a site, it's especially important to avoid broad questions. If you seed the site with broad questions — which so far seems alarmingly to be the case — then you're just reinventing Wikipedia, not creating a site that's likely to attract experts.  
> **username_2**: @Gilles'SO-stopbeingevil' Yea we've overdone it a bit but in general a site should go from more broad to more specific over time. We've over-saturated the broad side of the spectrum currently though, but are actively working to rectify that issue  
> **username_1**: @mousetail “a site should go from more broad to more specific over time” no, absolutely not. Broad questions will come naturally. Seeding the site with broad question during the early beta is a sure fire way to make it boring because it's just rehashing content that you can find elsewhere.  
> **username_2**: @Gilles'SO-stopbeingevil' I'm trying to prevent the issue that SO has where old the canonical close targets don't actually ask the question they became canonical for, but instead ask for some very specific sub-issue. It's better to purposefully ask these broad questions then let them arise organically when you have no choice about the content and good answers may be posed on terrible questions  
---
## 4 answers
### Answer by username_3 on 2023-05-17. Score: 13
# Mark-and-sweep collectors

Simple mark-and-sweep collectors attach a separate boolean to every allocated "object", and somehow keep track of every living object (you can imagine this as a big list of pointers, but they're usually slightly cleverer — it doesn't matter for this description). They also have an idea of what "roots" are accessible directly - these are things like global variables, local scopes that are currently running, whatever is applicable to the language.

The collection phase has four steps:

1. **Stop the world**: prevent anything else in the program from happening while the collection runs. Usually the collector runs right before an object would be allocated, and just holds up the rest of the program until it returns, which could be a while.
2. Go through *every* living object and **set the marker boolean to false**. Commonly this property is actually established at creation and reset at the end of the collection, but I'll list it here explicitly.
3. Starting at each root

   1. Set its marker to true.
   2. For each reference contained in this object (in a field, the surrounding scope, ...), perform step 3 with that object as the root. If the marker is already set, just move on to the next object.

   Once every root has been processed, the "mark" phase is complete.
4. Go through every object again, and **release any object whose marker isn't set**. This object wasn't reachable from any of the roots, so we know the program can never access it again.

   If the marker was set, unset it ready for the next collection. This is the "sweep" phase.

This is the model for a single-threaded collector with no optimisations. Slightly smarter mark-and-sweep collectors have multiple levels of marking, move objects around, and so on, but the core approach works like this.

## Tri-colour mark and sweep

In particular, a tri-colour system is a smarter mark-and-sweep system with three levels of marker instead of two: grey objects are known to be reachable from the roots, black objects are known to be reachable *and* have had all of their children marked grey, and white objects haven't been examined yet. These markers can be updated piecewise: pick a grey object, mark it black, and mark every white object it references as grey.

When there are no grey objects left, all the white ones are objects that couldn't be reached from the root, so they are freed and the process starts again.

This process can happen in multiple steps, each only moving a few objects from grey to black, so there doesn't need to be one big pause to look at everything. Every object is inspected at some point, however.

## Advantages and Disadvantages

There are two main advantages of this approach:

1. It is guaranteed to free every unreachable object, even if it's within a reference cycle.
2. It's fairly straightforward to implement.

For these reasons it's not uncommon for a mark-and-sweep phase to be included *within* more advanced collectors. The disadvantages tend to outweigh the benefits as the main strategy, however:

1. It's slow. It has to inspect every object in the system multiple times, every time.

   Tri-colour marking spreads the work out over time, but it still does it. In some situations, like user interfaces, one big pause is unacceptable but many smaller ones are ok, but in others this doesn't make much difference.
2. It relies on stopping every thread until the collection is done.
3. It has poor memory locality because it is always jumping between objects.
4. The naïve releasing strategy I described tends to fragment memory quite badly.

When there is a relatively small number of objects (such as within a nursery of a generational collector), or where the completeness properties are desired (such as for occasionally reclaiming reference cycles left by a more efficient reference-counting collector) it can be useful. For cases where raw performance isn't very important but catching escaped reference cycles is, this is a sensible cheap-to-implement strategy.
---
### Answer by username_4 on 2023-05-16. Score: 12
# Generational Garbage Collection

Generational garbage collection is a great tool in a language where small amounts of data are created and destroyed constantly, such as in a purely functional language (where "slightly modified" versions of data are constantly being constructed). The idea is simple: Most data will either die young or live a very long time, so if we optimize for freeing *recently allocated* memory, that will handle the most common cases. The popular Haskell compiler GHC uses one of these on the backend.

In its simplest form, a generational garbage collector has two generation: short-term storage and long-term storage, with the former being a much smaller data store. All new data is allocated in short-term storage. When short-term storage gets full, we run a mark-and-sweep garbage collection pass on only that small collection of data, freeing anything that we deem unreachable. If, after that pass, short-term storage is still occupied above some threshold, then we move all of that data to long-term storage and start a new, empty short-term storage region (in practice, this "move" is trivial and simply involves marking that buffer as long-term and creating a new one elsewhere). We monitor the total occupied space of long-term storage and run a full mark-and-sweep pass on *all* of our memory if it gets too full, but the idea is that this happens very infrequently.

The fundamental invariant of generational garbage collection is: Data can never point to data in a younger generation.

In a language with immutable data such as Haskell, this is a very simple, elegant algorithm, and short-term sweeps are fast. Since data is immutable[1], it's impossible for data to point to anything allocated after it. That is, nothing in long-term storage can ever point to short-term storage, so when we do a short-term sweep, we don't even have to think about long-term storage. Data can be cyclic, so data in short-term can point to other short-term data, if both were allocated at the same time. This can arise when [tying the knot](https://wiki.haskell.org/Tying_the_Knot), for instance. But cycles can never span generations.

In a language with mutable data, this requires some special handling when a pointer is reassigned. If we take some data in long-term storage and reassign it so that it points to data in short-term storage, we either have to make a global note of that for the mark-and-sweep algorithm, or we have to regress the long-term data back into short-term storage.

---

[1] With the exception of `IORef`, which already has special handling.
---
### Answer by username_5 on 2023-05-16. Score: 9
# Pure ARC

This is what Swift uses, and many (but not all) Objective-C projects.

ARC stands for “automated reference counting”. It automatically keeps track of how many references there are to an object, and deallocates the memory when that number hits 0.

## Advantages

* Fast: Because there’s no need for a garbage collector to perform tracing or seek out unreferenced objects, there is significantly less processor and even memory overhead. When Apple introduced GC to Objective-C, they never implemented it for iOS, citing that ARC was more power-friendly on mobile devices.
* Simple: It’s very straightforward to implement reference counting from scratch.

## Disadvantages

* Reference cycles: ARC by itself will never deallocate two objects that reference each other. Thus, you need the concept of a *weak reference*: a reference that doesn’t affect the refcount. Knowing when to use weak references puts more burden on the programmer and can be confusing to beginners.
* Frequency: If two objects pass the same reference back and forth, this can cause a lot of “ARC traffic” for something that won’t be deallocated. Not to say that no other GC system does this, but ime a lot of real-time Swift code goes out of its way to minimize changes to the refcount.
* Depth: If you delete the root of a very deep object tree, the chain of deallocations can blow the call stack. In some cases it’s possible to avoid this by e.g. scheduling the deallocation to occur on the next cycle of the run loop, but not all ARC languages give you control over this.
> **Comments:**
> **username_2** (2023-05-16): PHP uses reference counting but it's OK because it's intended for short-running scripts  
> **username_5** (2023-05-16): @mousetail Some garbage collectors use refcount + something else. That’s why I specified “_pure_ ARC” in the title, to distinguish it from RC + other.  
> **username_2** (2023-05-16): PHP has pure refcount. I don't know any garbage collector that does not have refcount, it's the basis for pretty much every other system  
> **username_5** (2023-05-16): I don’t know much about how GC works for a lot of modern languages. I’m sure there’s some weird stuff out there I’d never think of.  
> **username_7** (2023-05-17): [PHP does have a GC which collects cycles](https://www.php.net/manual/en/features.gc.collecting-cycles.php).  
> **username_6** (2023-09-28): Another variation on weak references is to have counters for "how many objects are interested in an object" and "how many objects hold references to an object", recognizing that many objects will need to hold references to objects that they're not particularly interested in, as long as anything else is interested in them.  For example, if an object exists to record the last time an entity performed an action satisfying some criterion, the entity would need to hold a reference to it so it can let it know when potentially interesting actions occur, but would have no interest in the object...  
> **username_6** (2023-09-28): ...once nothing else was interested in knowing about the monitored actions.  Cycles among references that are interested in each other are less common than situations where cyclic paths exist that would involve traversing different kinds of references.  
> **username_5** (2023-09-28): @supercat That just sounds like a different policy on when to use `weak` (Swift keeps separate strong and weak refcounts).  
> **username_6** (2023-09-28): @Bbrk24: In the approach I describe, the "weak-ish" references could still be used to identify objects even after it's discovered that that nobody is "interested" in them.  I don't know about whether that's possible in Switft; in .NET such semantics could be achieved by having a long weak reference to an object and a ConditionalWeakTable which ties the target of that reference to a finalizable object which is unreferenced anywhere else, and contains a reference to the long weak reference target.  
---
### Answer by username_8 on 2023-05-17. Score: 4
Some people argue (to death ) Reference Counting is *not* a Garbage Collection technique, but the [GC Handbook](http://gchandbook.org/) lists it, discusses and compares, and that's **the** definitive reference.

[A unified theory of garbage collection](https://dl.acm.org/doi/10.1145/1028976.1028982) paper goes a bit further and shows the duality between ("pure") tracing GC and RC, while collectors used in practice implement some "hybridisation" of both.

So we can establish the first dimension of the GC (design) space: on one side tracing GCs that traverse *live* objects, and opposite to them RC that traverses *dead* objects (imaging you have a huge tree with no links to nodes except from a parent node, and now the root of the tree becomes unreachable — reference counter goes 0, you deallocate the node and decrease counters of all it's children, which go 0 too, so you now have to deallocate them too, and so on until you traverse and deallocate all the now-garbage; while tracing GC would evacuate live objects and deallocate the whole region of memory regardless of which objects are dead inside it and how they are interconnected).

Another dimension is concurrency and parallelism, which are different notions. A *parallel* GC can use several cores at the same time to mark live objects and/or move/copy and/or free/compact memory. While *concurrent* GC can do that *while program does it work* and allocates new objects/forgets about the old ones. Concurrent GC might do that in a single thread or in a parallel fashion. There's also a notion of *incrementality*: incremental GC can do some work, then stop, then resume from the same spot. Some people use "concurrent" and "incremental" as synonyms — obviously, if you have an incremental GC you can turn it into concurrent by running preemptively with mutators.

But these are kinda *internal* GC properties, from an external point of view, the main characteristics of a GC are throughput, latency and space (overhead).

Throughput (as per usual) is how much garbage a collector can free per (sizeable) unit of time. It's very important if an application works in bursts allocating lots of temporary objects that need to be quickly freed before the next burst. Often Stop-the-World (STW) *parallel* collectors provide highest throughput.

Most usual throughput conflicts with *latency* i.e. how long a mutator thread have to wait for the GC to do its work before mutator can continue doing something useful. Modern GCs like the Java's Z and Shenandoha exhibit extremely low latencies.

Finally most GCs (RCs might show a counterexample) incur some memory overhead. The most obvious case is two-space copying collector that requires twice as much memory as the application otherwise would use.

And that's just the general very superficial picture, while there are **lots** of implementation details and programming language and application and workload specifics.

That said, I'd claim that well-designed and well-optimized tracing GC is overall more performant and beneficial that (general) reference counting. But very hard to implement. Still "serious languages" like Kotlin Native and AssemblyScript went from an RC to a tracing GC (somewhat recently).
---
