<p>TL;DR: Could a programming language that uses manual memory management use a tracing GC to rescue the usual suspects of memory leaks?</p>
<p>I was reading about the Boehm GC and came across <a href="https://www.hboehm.info/gc/leak.html" rel="nofollow noreferrer">this page</a> that talks about using the Boehm GC to &quot;detect leaks&quot;:</p>
<blockquote>
<p>The garbage collector may be used as a leak detector. In this case, the primary function of the collector is to report objects that were allocated (typically with GC_MALLOC), not de-allocated (normally with GC_FREE), but are no longer accessible. Since the object is no longer accessible, there in normally no way to de-allocate the object at a later time; thus it can safely be assumed that the object has been &quot;leaked&quot;.</p>
</blockquote>
<p>I have never used the Boehm GC, so it is possible I am missing something when I read the above statement and don't know how successful the Boehm GC is in &quot;real world&quot; usage is and what its limitations are. But it seems it can tell if an object is &quot;leaking&quot;. The page uses the words &quot;normally&quot; and &quot;typically&quot; but the devil is in the details, details I am not familiar with.</p>
<p>Take the above and couple this idea that Python primarily uses reference counting supplemented by a tracing GC to manage memory. The point I am focusing on is this: tracing GCs can work with other memory management techniques (not sure how if this is rendered mute by python's infamous reputation when it comes to memory).</p>
<p>I was thinking the above two ideas side by side and wondering: <strong>Can tracing GCs work alongside manual memory management in the same way the tracing GC in python works alongside refc?</strong></p>
<p>Could something like the Boehm GC be extended or has there been work to extend GCs to work alongside manual memory management. So, perhaps benefiting from the speed of manual memory management with the cushion of tracing GCs to fall back on albeit at a cost - while the programmer can deal with leaks as they are detected.</p>
<p>The idea sounds too layman to be novel, so I suspect my question might be better asked as: what are the limitations of a hybrid tracing GC + manual memory management approach?</p>

<garbage-collection>
What are the limitations of combining tracing GC with manual memory management?
CC BY-SA 4.0
2024-04-08T14:02:30.123Z
The issue I can foresee is that when memory is manually freed, the programmer will also need to ensure that any latent references to that memory are zeroed out, not just unused. Otherwise the GC will see references which might now point to a different allocation (at the same address) and think that they keep that allocation alive.
kaya3
<p>Boehm GC works quite well in that mode -- you can use GC_MALLOC to allocate memory and use GC_FREE to free (some of) the memory (manual management), while letting the garbage collector free the rest.  By using GC_FREE to free memory when possible/easy you reduce the overhead of the garbage collector -- it does not need to collect as often.</p>
<p>So that's mainly just an advantage -- better performance than &quot;pure&quot; garbage collection while dealing with memory that is not manually freed as well as just using the garbage collector.</p>
<p>The only disadvantage I see is that the garbage collector needs to be designed to support this, as well as the normal problems with a conservative collector (false retention) caused by garbage collection being an add-on not natively supported by the language.</p>

CC BY-SA 4.0
2024-04-07T06:32:40.683Z
2024-04-07T08:16:27.600Z
2024-04-07T08:16:27.600Z
Chris Dodd
<p>Tracing garbage collectors and manual lifetime management are different tools with different but overlapping uses.  Neither should be viewed as making the other unnecessary.</p>
<p>Object instances that encapsulate immutable state are best managed with tracing garbage collectors.  In most cases were multiple entities might each hold a reference to the same immutable object, none would have any reason to know or care about the others, nor any reason to care about whether or one it might become the last entity to hold a reference.</p>
<p>Objects that encapsulate a responsibility to inform some outside entity when its &quot;services&quot; are no longer needed are best managed with a deterministic scheme where objects' useful lifetimes are precisely delineated, so that there will be no doubt about when the outside entities receive their notifications.</p>
<p>Managing even those objects with a combination of a tracing GC and explicit lifetime management is useful not only because it may allow accidentally abandoned objects to eventually fulfill their outside responsibilities, but more importantly because any reference is guaranteed to identify the same object unless or until the reference itself is overwritten.  When using manual lifetime management without a tracing GC, by contrast, destroying an object without deleting all references may result in dangling references to a destroyed object effectively morphing into seemingly-valid references to some unrelated new object.  Even if e.g. a closed <code>File</code> object would be useless, having attempts to use it deterministically fail would be much better than having such attempts write data to some other file that happened to be opened after the original was closed.</p>

CC BY-SA 4.0
2024-12-19T21:15:42.653Z
2024-12-19T21:15:42.653Z
supercat
<h3>Is there room for GCs in manual memory management?</h3>
<p><strong>Yes</strong>.</p>
<p>First of all, reference-counting, even without a tracing GC to collect cycles, is typically considered a form of GC itself. It is imperfect, in that it may not collect cycle unless augmented, but it is memory safe.</p>
<p>Secondly, there is also a host of schemes for a variety of circumstances. While arenas/bump allocators may not be thought of as GCs, I'd argue they're somewhat close to them at least. And of course there's the whole suite of &quot;solutions&quot; to assist with concurrent operations:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Read-copy-update" rel="noreferrer">Read-Copy-Update (RCU)</a> is a GC mechanism used in the Linux kernel.</li>
<li><a href="https://en.wikipedia.org/wiki/Hazard_pointer" rel="noreferrer">Hazard Pointers</a> are used in lock-free data-structures.</li>
<li>Epoch-Based Reclamation (EBR) and Quiescent-State-Based Reclamation (QSBR) are also used in lock-free data-structures, in various flavors.</li>
</ul>
<p>There's a very diverse ecosystem of various solutions to memory reclamation for a variety of situations -- often involving concurrency.</p>
<h3>Is there room for a Tracing GC in manual memory management?</h3>
<p><strong>Yes</strong>.</p>
<p>Very much like in Python, reference-counting can be helpfully supplemented by a cycle collector. Collecting cycles involves tracing, and thus a (limited in scope) Tracing GC is involved in such a case.</p>
<p>There are implementation difficulties. In most manual memory management languages, there's no <em>language facility</em> to actually trace, and a tracing implementation regularly needs to trace across manually managed memory -- as a simple example, think of a <code>Traced&lt;Foo&gt;</code> object, where <code>Foo</code> contains a <code>Vec&lt;Traced&lt;Bar&gt;&gt;</code>. And of course there's the whole root identification issue. In such a case, the user cooperation is typically required, which leaves some room for error. Nonetheless, the result is a (limited in scope) Tracing GC.</p>
<p>Beyond cycle collecting, however, I have seen attempts at defining &quot;limited-in-scope&quot; GCs for usecases where GCs are useful: graphs, and lock-free collections.</p>
<p>Those GCs typically have a very small scope -- sometimes managing a single graph, or a single collection -- meaning that multiple GCs, sometimes of different implementations, may coexist in the same process. If they remain strictly separate, it's actually relatively simple. If they are interleaved, the challenges of tracing and rooting occur again.</p>
<h3>What about a full GC?</h3>
<p>This exists, too. As mentioned by @Chris Dodd, the Boehm GC can work both as a complete memory management solutions -- the user allocates, the GC handles the rest -- or as a hybrid solution -- the user allocates and sometimes deallocates, the GC handles the rest.</p>
<p>The main advantage of the conservative Boehm GC here is that it automatically handles tracing and rooting without user intervention, by using a conservative tracing approach including stack tracing. A conservative scan has some shortcomings -- not recognizing pointers stored in non-aligned memory, or stored as integers -- which impose limitations on the application code, but those are not very restrictive.</p>
<p>In hybrid mode, there's still clearly manual memory management going on.</p>

CC BY-SA 4.0
2024-04-07T10:55:05.533Z
2024-04-07T10:55:05.533Z
Matthieu M.
# What are the limitations of combining tracing GC with manual memory management?
Posted by: username_0 on 2024-04-07
TL;DR: Could a programming language that uses manual memory management use a tracing GC to rescue the usual suspects of memory leaks?

I was reading about the Boehm GC and came across [this page](https://www.hboehm.info/gc/leak.html) that talks about using the Boehm GC to "detect leaks":

> The garbage collector may be used as a leak detector. In this case, the primary function of the collector is to report objects that were allocated (typically with GC\_MALLOC), not de-allocated (normally with GC\_FREE), but are no longer accessible. Since the object is no longer accessible, there in normally no way to de-allocate the object at a later time; thus it can safely be assumed that the object has been "leaked".

I have never used the Boehm GC, so it is possible I am missing something when I read the above statement and don't know how successful the Boehm GC is in "real world" usage is and what its limitations are. But it seems it can tell if an object is "leaking". The page uses the words "normally" and "typically" but the devil is in the details, details I am not familiar with.

Take the above and couple this idea that Python primarily uses reference counting supplemented by a tracing GC to manage memory. The point I am focusing on is this: tracing GCs can work with other memory management techniques (not sure how if this is rendered mute by python's infamous reputation when it comes to memory).

I was thinking the above two ideas side by side and wondering: **Can tracing GCs work alongside manual memory management in the same way the tracing GC in python works alongside refc?**

Could something like the Boehm GC be extended or has there been work to extend GCs to work alongside manual memory management. So, perhaps benefiting from the speed of manual memory management with the cushion of tracing GCs to fall back on albeit at a cost - while the programmer can deal with leaks as they are detected.

The idea sounds too layman to be novel, so I suspect my question might be better asked as: what are the limitations of a hybrid tracing GC + manual memory management approach?
**Tags:** garbage-collection
> **Comments:**
> **username_1**: The issue I can foresee is that when memory is manually freed, the programmer will also need to ensure that any latent references to that memory are zeroed out, not just unused. Otherwise the GC will see references which might now point to a different allocation (at the same address) and think that they keep that allocation alive.  
---
## 3 answers
### Answer by username_4 on 2024-04-07. Score: 8 | Accepted answer
### Is there room for GCs in manual memory management?

**Yes**.

First of all, reference-counting, even without a tracing GC to collect cycles, is typically considered a form of GC itself. It is imperfect, in that it may not collect cycle unless augmented, but it is memory safe.

Secondly, there is also a host of schemes for a variety of circumstances. While arenas/bump allocators may not be thought of as GCs, I'd argue they're somewhat close to them at least. And of course there's the whole suite of "solutions" to assist with concurrent operations:

* [Read-Copy-Update (RCU)](https://en.wikipedia.org/wiki/Read-copy-update) is a GC mechanism used in the Linux kernel.
* [Hazard Pointers](https://en.wikipedia.org/wiki/Hazard_pointer) are used in lock-free data-structures.
* Epoch-Based Reclamation (EBR) and Quiescent-State-Based Reclamation (QSBR) are also used in lock-free data-structures, in various flavors.

There's a very diverse ecosystem of various solutions to memory reclamation for a variety of situations -- often involving concurrency.

### Is there room for a Tracing GC in manual memory management?

**Yes**.

Very much like in Python, reference-counting can be helpfully supplemented by a cycle collector. Collecting cycles involves tracing, and thus a (limited in scope) Tracing GC is involved in such a case.

There are implementation difficulties. In most manual memory management languages, there's no *language facility* to actually trace, and a tracing implementation regularly needs to trace across manually managed memory -- as a simple example, think of a `Traced` object, where `Foo` contains a `Vec>`. And of course there's the whole root identification issue. In such a case, the user cooperation is typically required, which leaves some room for error. Nonetheless, the result is a (limited in scope) Tracing GC.

Beyond cycle collecting, however, I have seen attempts at defining "limited-in-scope" GCs for usecases where GCs are useful: graphs, and lock-free collections.

Those GCs typically have a very small scope -- sometimes managing a single graph, or a single collection -- meaning that multiple GCs, sometimes of different implementations, may coexist in the same process. If they remain strictly separate, it's actually relatively simple. If they are interleaved, the challenges of tracing and rooting occur again.

### What about a full GC?

This exists, too. As mentioned by @Chris Dodd, the Boehm GC can work both as a complete memory management solutions -- the user allocates, the GC handles the rest -- or as a hybrid solution -- the user allocates and sometimes deallocates, the GC handles the rest.

The main advantage of the conservative Boehm GC here is that it automatically handles tracing and rooting without user intervention, by using a conservative tracing approach including stack tracing. A conservative scan has some shortcomings -- not recognizing pointers stored in non-aligned memory, or stored as integers -- which impose limitations on the application code, but those are not very restrictive.

In hybrid mode, there's still clearly manual memory management going on.
---
### Answer by username_2 on 2024-04-07. Score: 4
Boehm GC works quite well in that mode -- you can use GC\_MALLOC to allocate memory and use GC\_FREE to free (some of) the memory (manual management), while letting the garbage collector free the rest. By using GC\_FREE to free memory when possible/easy you reduce the overhead of the garbage collector -- it does not need to collect as often.

So that's mainly just an advantage -- better performance than "pure" garbage collection while dealing with memory that is not manually freed as well as just using the garbage collector.

The only disadvantage I see is that the garbage collector needs to be designed to support this, as well as the normal problems with a conservative collector (false retention) caused by garbage collection being an add-on not natively supported by the language.
---
### Answer by username_3 on 2024-12-19. Score: 0
Tracing garbage collectors and manual lifetime management are different tools with different but overlapping uses. Neither should be viewed as making the other unnecessary.

Object instances that encapsulate immutable state are best managed with tracing garbage collectors. In most cases were multiple entities might each hold a reference to the same immutable object, none would have any reason to know or care about the others, nor any reason to care about whether or one it might become the last entity to hold a reference.

Objects that encapsulate a responsibility to inform some outside entity when its "services" are no longer needed are best managed with a deterministic scheme where objects' useful lifetimes are precisely delineated, so that there will be no doubt about when the outside entities receive their notifications.

Managing even those objects with a combination of a tracing GC and explicit lifetime management is useful not only because it may allow accidentally abandoned objects to eventually fulfill their outside responsibilities, but more importantly because any reference is guaranteed to identify the same object unless or until the reference itself is overwritten. When using manual lifetime management without a tracing GC, by contrast, destroying an object without deleting all references may result in dangling references to a destroyed object effectively morphing into seemingly-valid references to some unrelated new object. Even if e.g. a closed `File` object would be useless, having attempts to use it deterministically fail would be much better than having such attempts write data to some other file that happened to be opened after the original was closed.
---
