
How the Boehm Garbage Collector Works - int3
http://discontinuously.com/2012/02/How-the-Boehm-Garbage-Collector-Works/
======
__alexs
Does anyone have data on how much it leaks in practice? Also, could
maliciously crafted input result in a DoS vector for Boehm using applications?

~~~
ghusbands
My story is similar to nield's; I worked on a project that used the Boehm-
Weiser collector in a 32-bit process.

Once or twice a tear, we'd have an unbounded memory growth to trace. Most of
the time, we were able to solve it by providing layouts for structures that
mixed pointers and pointer-like data. The debug facility it provides for
providing a random backtrace to a root was helpful in those cases.

However, more than once we had a leak that it was beyond our ability to trace,
despite a significant amount of diagnostic work. We found some workarounds
that were specific to our app, which helped.

In the end, the costs to us were high enough that we rewrote our codebase in
C# (our software was Windows-only). It was pretty much a line-for-line
conversion, but it worked and we no longer had any memory leaks (except for
one, in .NET timers, that took only minutes to trace using readily-available
diagnostic tools).

I do believe a 64-bit process using the Boehm collector would be much less
likely to suffer these issues, but this was before x64 was prevalent in
corporate server rooms.

On your second question: Crafted input can indeed result in significant memory
retention, if you aren't careful to exclude user input from the GC and have
large, cyclic structures to collect, on a 32-bit machine, with almost any text
encoding.

~~~
StavrosK
Correct me if I'm wrong, but the GC isn't the _only_ thing that frees memory,
is it? You can still free it manually (and are supposed to), so those leaks
would have happened without a GC too, and it can only help things.

I'm getting the impression that posts here imply you're better off without a
GC, I'm not sure if that's the intention, but it strikes me as wrong.

~~~
ghusbands
Your parent post spoke about going from one GC to another and having the
problems disappear. Hence, it only implies that changing GC can have a useful
effect.

Your message does betray some confusion about garbage collection. The basic
principle of GC is that you do not deallocate RAM manually (and, under most
GCs, cannot), and programs written under it expect deallocation to happen
automatically. You are not 'supposed to' free manually, as that would be
wasted effort, and some standard configurations of the Boehm collector
actually ignore all calls to free().

There is a very useful description of garbage collection at
[http://blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047...](http://blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586.aspx)
which includes and expands on the useful mental model that "garbage collection
is simulating a computer with an infinite amount of memory".

------
loeg
One of the biggest problems with this style of "scan all memory" GC is that
you have to stop all threads (flush all pointers to memory) to perform a
collection. Pointers held in registers in other active threads are
inaccessible to the GC thread.

Failing to stop the world (or ensure all pointers are in memory rather than
registers) can result in live objects being collected. This happened to Ruby
at one point: <http://timetobleed.com/the-broken-promises-of-mrireeyarv/>

Edit: This boils down to breaking the compiler's model of memory as specified
by the C standard; all bets are off.

~~~
cbsmith
Um no. First, it doesn't "scan all memory". It scans the root and fans out.
You can do read/write barriers and such to avoid blocking GC. It doesn't
matter what value registers have in them. What matters is what is on the stack
and what is reachable from said stack. If you have control of malloc, you can
be far more clever than what you are describing. Ruby had a crummy
implementation of GC, but you'll notice, for example, that most JVM's do "scan
all memory" without forcing all active threads to be blocked at once.

------
amatus
This would fail badly on a <http://en.wikipedia.org/wiki/XOR_linked_list>

~~~
kmm
You really shouldn't be using a XOR linked list anyway. It has a lot of
disadvantages and none of them weigh up to the only advantage of a lower
memory footprint per list item.

~~~
reitblatt
The point is that this garbage collector is unsound. It may collect memory
that is still in use. That's bad.

~~~
reitblatt
To be clearer: safety is no longer compositional in this system. To safely use
the Boehm garbage collector, it's not sufficient that my own code doesn't use
"disguised" pointers: I have to verify that my libraries don't either. That's
impractical and contrary to good software engineering at best, impossible at
worst.

~~~
rayiner
No, you have to verify that your libraries don't keep disguised pointers to
objects you allocated via the GC, that you don't yourself keep pointers to.
It's quite unusual in C to give a reference to an object to another piece of
code then drop all the pointers to it that you hold, because the usual
discipline is that the module that allocates an object frees it.

~~~
reitblatt
Not if you build the GC with --enable-redirect-malloc to use with existing
libraries/code bases. Which should be fine because, after all, this _is_ a
"conservative" garbage collector. Right?

------
kmm
I've often wondered how a garbage collector can scan the entire memory in a
reasonable time. The article doesn't explain that particular operation. With a
quick look at the source code I couldn't figure it out either.

Can anyone enlighten me? Is it only the stack that is checked because that
seems possible in a reasonable time.

~~~
loeg
Boehm controls malloc; it knows where the heap is. In general, yes, the more
memory you have allocated, the slower BoehmGC will be.

It checks both stack and heap.

------
vilda
Neither in the article nor it the paper they mention cycles in references.
Does it solve it in any way?

~~~
chrisb
Garbage collectors have no problem with reference cycles.

A GC works by finding all objects that are reachable (referenced by some other
object or a root), then discarding all other objects. So a cyclic data
structure is completely discarded as soon as there are no live references to
any of its objects.

As usual, Wikipedia has lots more information:
[http://en.wikipedia.org/wiki/Garbage_collection_(computer_sc...](http://en.wikipedia.org/wiki/Garbage_collection_\(computer_science\))

GC roots are generally things like: static/global variables, active stack
frames (function parameters and local variable), CPU registers.

