LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-27-2009, 04:32 PM   #1
codeNinja
LQ Newbie
 
Registered: Aug 2009
Posts: 6

Rep: Reputation: 0
Implementing a Garbage Collector in C++


Hello,

I'm interested in writing a generational garbage collector in C++. This is for a Python-like programming language project. I have used Boehm's garbage collector before, but I'm worried that it will have noticeable pause times (unacceptable for interactive programs). Since I can't really find any other C++ established GCs out there, I have been thinking of simply writing my own.

However, there are two essential ingredients I need for this:
- Some kind of write barrier mechanism that will notify my collector when something is written to a part of the heap
- A way for me to know the addresses and sizes of the stack, heap and global storage (the root sets)

Would anyone happen to know how to implement write barriers (and how to get the stack, heap and global storage information) on the Linux platform?

As an alternative, if you know of C++ GCs other than Boehm, I would also be interested.
 
Old 08-27-2009, 07:04 PM   #2
jiml8
Senior Member
 
Registered: Sep 2003
Posts: 3,171

Rep: Reputation: 116Reputation: 116
Is there a reason why you cannot use the system's garbage collection? Linux will clean up the mess when the program shuts down or abends.
 
Old 08-27-2009, 07:24 PM   #3
codeNinja
LQ Newbie
 
Registered: Aug 2009
Posts: 6

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by jiml8 View Post
Is there a reason why you cannot use the system's garbage collection? Linux will clean up the mess when the program shuts down or abends.
If you have to ask that, you probably don't know what I mean by garbage collection.

I'm implementing a scripting language with built-in garbage collection (as with Java or Python). Active collection of unreferenced objects is required. Otherwise, a long running program will just fill the heap. See:

http://en.wikipedia.org/wiki/Garbage...puter_science)

Last edited by codeNinja; 08-27-2009 at 07:26 PM.
 
Old 08-27-2009, 09:29 PM   #4
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,781

Rep: Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081
Quote:
However, there are two essential ingredients I need for this:
- Some kind of write barrier mechanism that will notify my collector when something is written to a part of the heap
- A way for me to know the addresses and sizes of the stack, heap and global storage (the root sets)
If you are implementing a scripting language wouldn't the "stack" be stored as a variable in the C++ interpreter? And wouldn't the interpreter know when it interprets an instruction that writes to the "heap" or "stack"? Otherwise I guess you could use mprotect and catch the segfaults.
 
Old 08-27-2009, 09:52 PM   #5
graemef
Senior Member
 
Registered: Nov 2005
Location: Hanoi
Distribution: Fedora 13, Ubuntu 10.04
Posts: 2,379

Rep: Reputation: 148Reputation: 148
Maybe you could consider using auto_ptr, rather than a full blown garbage collector? I think that this would work so long as you restrict your project to avoid some of the messier things that you can do in C++ such as changing the type dereferencing them hiding the pointer in a union etc. But if you can control how your pointers are used then this could be a start. There is a boost library (smart pointers) that you may want to look at.
 
Old 08-27-2009, 11:59 PM   #6
PatrickNew
Senior Member
 
Registered: Jan 2006
Location: Charleston, SC, USA
Distribution: Debian, Gentoo, Ubuntu, RHEL
Posts: 1,148
Blog Entries: 1

Rep: Reputation: 48
The website of the Boehm's GC (http://www.hpl.hp.com/personal/Hans_Boehm/gc/) states that it works with pthreads under Irix, Linux, Solaris, HP-UX and TRU64, as well as the native threads on Windows and OS X. So to eliminate pauses, you could just push Boehm's into another thread and let the OS handle the multitasking for you. After all, your program would experience some stalls from the fact that the PC is running other programs, and splitting the GC into another thread will keep GC "pauses" in that same time scale - imperceptible to the user.
 
Old 08-28-2009, 09:18 AM   #7
codeNinja
LQ Newbie
 
Registered: Aug 2009
Posts: 6

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by ntubski View Post
If you are implementing a scripting language wouldn't the "stack" be stored as a variable in the C++ interpreter? And wouldn't the interpreter know when it interprets an instruction that writes to the "heap" or "stack"
Yes and no. For one, I'm implementing a JIT compiler and not just an interpreter. That makes it slightly more complicated. Another issue is that I may want to, say, use C++'s std::vector<> or std::string<> to implement some of my core language types (I would then use a special GC allocator). These types, however, will not notify me when they write to specific memory areas. In general, I think it's just more convenient when your garbage collector can work as a replacement for "new". Otherwise you need to keep track of different kind of "pointer" objects all the time.

Quote:
Originally Posted by ntubski View Post
Otherwise I guess you could use mprotect and catch the segfaults.
That could work but it sounds kind of slow. How do you actually catch segfaults? If you need some kind of event loop, then it's out of the question.

Quote:
Maybe you could consider using auto_ptr, rather than a full blown garbage collector? I think that this would work so long as you restrict your project to avoid some of the messier things that you can do in C++ such as changing the type dereferencing them hiding the pointer in a union etc. But if you can control how your pointers are used then this could be a start. There is a boost library (smart pointers) that you may want to look at.
The problem with smart pointers and reference counting is that objects forming cycles of references will never be deallocated. This approach only works for "toy" programming languages, or scripts that only run for a short time.

Quote:
The website of the Boehm's GC (http://www.hpl.hp.com/personal/Hans_Boehm/gc/) states that it works with pthreads under Irix, Linux, Solaris, HP-UX and TRU64, as well as the native threads on Windows and OS X. So to eliminate pauses, you could just push Boehm's into another thread and let the OS handle the multitasking for you. After all, your program would experience some stalls from the fact that the PC is running other programs, and splitting the GC into another thread will keep GC "pauses" in that same time scale - imperceptible to the user.
I'm pretty sure Boehm's GC will just pause every thread (but itself) during a collection phase.
 
Old 08-28-2009, 10:15 AM   #8
codeNinja
LQ Newbie
 
Registered: Aug 2009
Posts: 6

Original Poster
Rep: Reputation: 0
Ok, well, I did some more research. It seems mprotect might be a viable option. However, I'm still not sure which signal to catch, and how to handle it. I need to know which page a write has been attempted to, and I need to somehow allow the write to proceed normally after that. Help with this would be appreciated.

As for the heap, stack and global storage bounds. It seems the /proc/self/maps contains some of that information... There's a file marked [stack], whose upper address I assume is the permanent stack top address for the process. I know my own heap bounds because I will allocate a gc-managed "sub-heap" myself. I also assume that the globally stored variables never change address. Hence, the global storage addresses should be whatever is writable and not executable and not marked [stack] or [heap]. Does anyone see a flaw in this reasoning?
 
Old 08-28-2009, 11:00 AM   #9
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,781

Rep: Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081
Quote:
Originally Posted by codeNinja View Post
Yes and no. For one, I'm implementing a JIT compiler and not just an interpreter. That makes it slightly more complicated.
It is still the case that the JIT compiler knows which instructions it generates write to the "heap/stack".

Quote:
Another issue is that I may want to, say, use C++'s std::vector<> or std::string<> to implement some of my core language types (I would then use a special GC allocator). These types, however, will not notify me when they write to specific memory areas.
Maybe you would have to wrap them.

Quote:
In general, I think it's just more convenient when your garbage collector can work as a replacement for "new". Otherwise you need to keep track of different kind of "pointer" objects all the time.
If you want a GC like that use Boehms's. In general I think it's more convenient to use an existing scripting language/garbage collector than invent a new one, but if you feel you need it, nobody can stop you.

Quote:
That could work but it sounds kind of slow. How do you actually catch segfaults? If you need some kind of event loop, then it's out of the question.
Not an event loop, unix signals are asynchronous. When there is a write attempt to a protected page the program gets a segfault signal (also when it dereferences a NULL pointer), you can install a signal handler with sigaction

Perhaps this message on the gcc mailing list will be of interest to you:
Quote:
Originally Posted by Dmitry Antipov
The most non-straightforward garbage collection methods, including, but not limited to,
the most frequently used generational and incremental techniques, requires a 'write
barrier' or 'store barrier' - a piece of code which is executed when a pointer (within
one object, usually) to some another object is written. The following methods are
used widely:
  1. call barrier code explicitly when it's needed, determining such places
    by static analysis performed by the programmer;
  2. allocate objects from the heaps with OS 'page-aware' structures, then use OS
    memory protection for the underlying pages and handle protection faults;
  3. rely on the compiler support, which means the compiler should emit some
    code when the pointer store is generated.
#2 is the mprotect method, I was suggesting #3, #1 doesn't really make sense when you have an interpreter and runtime code generation.

Quote:
Originally Posted by CodeNinja
I'm pretty sure Boehm's GC will just pause every thread (but itself) during a collection phase.
This is not possible if the threads are native OS threads, user space cannot control the kernel scheduler. Of course, if you have a single core only one thread is running at a time, but the GC thread can still be time-sliced during the collection phase.
 
Old 08-28-2009, 11:42 AM   #10
PatrickNew
Senior Member
 
Registered: Jan 2006
Location: Charleston, SC, USA
Distribution: Debian, Gentoo, Ubuntu, RHEL
Posts: 1,148
Blog Entries: 1

Rep: Reputation: 48
I don't have a definitive answer, but I don't believe the Boehm's GC could stop other threads. Some pthreads implementations do have pthread_suspend() to pause other threads, but the dominant Linux implementation is not one of those, and Boehm's explicitly claims to support pthreads on Linux. I'm not certain what kind of pessimizations a GC would have to make in such a concurrent environment, but Boehms is either making those pessimizations or they are doing something very hackish to pause other threads.
 
Old 08-28-2009, 12:16 PM   #11
codeNinja
LQ Newbie
 
Registered: Aug 2009
Posts: 6

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by PatrickNew View Post
I don't have a definitive answer, but I don't believe the Boehm's GC could stop other threads. Some pthreads implementations do have pthread_suspend() to pause other threads, but the dominant Linux implementation is not one of those, and Boehm's explicitly claims to support pthreads on Linux. I'm not certain what kind of pessimizations a GC would have to make in such a concurrent environment, but Boehms is either making those pessimizations or they are doing something very hackish to pause other threads.
Pretty sure they're going with the "hackish" approach. Their concurrent GC, as far as I know, is a multithreaded GC (uses multiple threads to perform collection). It can't run at the same time as the mutator threads. Their incremental GC mode also needs to pause the mutator threads every once in a while.
 
Old 08-28-2009, 12:18 PM   #12
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by codeNinja View Post
This is for a Python-like programming language project.
In a Python-like language, you (as language implementor) should be in more control of pointers and/or references. You should use that control to do something much better than a write barrier to detect changed references.

Quote:
I have used Boehm's garbage collector before, but I'm worried that it will have noticeable pause times (unacceptable for interactive programs).
How big a pause would be noticable? "interactive" covers a wide range. For some kinds of interactive a 100ms pause would be OK; for other kinds a 10ms pause would be a problem.

Quote:
I have been thinking of simply writing my own.
Even if you write your own, avoiding the pause may be very hard. What is the central concept of the method you would use to avoid a pause?

Quote:
Some kind of write barrier mechanism that will notify my collector when something is written to a part of the heap
What fraction of the writes do you expect to be pointer updates vs. what fraction of the heap do you expect to need a write barrier at typical times?

In a language (like Python) with much less flexible pointer manipulation than C, it would be much simpler to load all pointer writes rather than load a heap position based subset of all writes. The load per write would also be much lower. Unless you expect the barrier to cover only a tiny fraction of writes, working at the pointer level would be faster as well as simpler.

Quote:
A way for me to know the addresses and sizes of the stack, heap and global storage (the root sets)
That would seem to be entirely in your control. Why would you want to rewrite something as hard as a GC without taking control of the memory pools. But if you do take control of the memory pools, you trivially know where they are. That also lets you exclude a lot of built in memory use (that you can manage more efficiently yourself) from the GC.

If you want a general GC for use in a language as flexible (rat pointer math) as C, do you really expect to be able to do better than Boehm's? If you want to make use of whatever ways your language has less pointer flexibility than C, then you need some focus on that.

Quote:
Originally Posted by codeNinja View Post
The problem with smart pointers and reference counting is that objects forming cycles of references will never be deallocated.
I would expect such cycles to be very rare. Certainly, you can't just ignore them and let lost memory build up when cycles occur. But it is a shame to load an entire design with large performance/complexity costs just to cover an obscure case.

It should be simpler to create a system based mostly on reference counting with a background GC (cooperating with the reference counting) to pick up memory lost to cycles.

In addition to catching almost all disconnected memory (so the GC would have much less work to do) the reference counting could also eliminate the need for anything like a write barrier.

Last edited by johnsfine; 08-28-2009 at 12:26 PM.
 
Old 08-28-2009, 12:29 PM   #13
codeNinja
LQ Newbie
 
Registered: Aug 2009
Posts: 6

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by johnsfine View Post
In a Python-like language, you (as language implementor) should be in more control of pointers and/or references. You should use that control to do something much better than a write barrier to detect changed references.
Like I said, that only works for objects I have control over... It's also somewhat error-prone (what if someone forgets to handle this properly somewhere, how hard will that be to trace), and finally, it might actually be slower in practice (I only need write barriers for the older generations).

Quote:
Originally Posted by johnsfine View Post
How big a pause would be noticable? "interactive" covers a wide range. For some kinds of interactive a 100ms pause would be OK; for other kinds a 10ms pause would be a problem.
Think Quake, the pause time should probably be in the range of 10-20ms of less. Boehm does offer a "target" pause time, but only in incremental mode with multithreaded collection disabled...

Quote:
Originally Posted by johnsfine View Post
Even if you write your own, avoiding the pause may be very hard. What is the central concept of the method you would use to avoid a pause?
Generational collection. Frequent but fast collections of the younger generations. Slower but rare collections of older generations (which could possibly be multithreaded at some point).

Quote:
Originally Posted by johnsfine View Post
What fraction of the writes do you expect to be pointer updates vs. what fraction of the heap do you expect to need a write barrier at typical times?
Only the older generations need write barriers. These write barriers can possibly be disabled once a page has been found to be "dirty" (it becomes part of the root set). I don't expect the pointer updates to be very frequent.

Quote:
Originally Posted by johnsfine View Post
Unless you expect the barrier to cover only a tiny fraction of writes, working at the pointer level would be faster as well as simpler.
It may be not be faster nor simpler.

Quote:
Originally Posted by johnsfine View Post
That would seem to be entirely in your control. Why would you want to rewrite something as hard as a GC without taking control of the memory pools. But if you do take control of the memory pools, you trivially know where they are. That also lets you exclude a lot of built in memory use (that you can manage more efficiently yourself) from the GC.
Obviously, whatever memory the VM allocates for other uses than program data does not need to be garbage collected.

Quote:
Originally Posted by johnsfine View Post
If you want a general GC for use in a language as flexible (rat pointer math) as C, do you really expect to be able to do better than Boehm's? If you want to make use of whatever ways your language has less pointer flexibility than C, then you need some focus on that.
I don't know if it can be faster for collections, but I'm pretty sure I can have smaller pause times and almost completely sure I can have faster allocation times.

Quote:
Originally Posted by johnsfine View Post
I would expect such cycles to be very rare. Certainly, you can't just ignore them and let lost memory build up when cycles occur. But it is a shame to load an entire design with large performance/complexity costs just to cover an obscure case.
Cycles are not "an obscure case". If you're storing any kind of graph in memory, you're very likely to have cycles.

Last edited by codeNinja; 08-28-2009 at 12:33 PM.
 
Old 08-28-2009, 02:38 PM   #14
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
Quote:
Originally Posted by codeNinja View Post
...
In general, I think it's just more convenient when your garbage collector can work as a replacement for "new". Otherwise you need to keep track of different kind of "pointer" objects all the time.
...
AFAIK in Perl unreferenced objects are destroyed when their scope is left. If this understanding is correct, garbage collector as 'new' replacement doesn't quite work.
 
Old 08-30-2009, 12:47 AM   #15
paulsm4
LQ Guru
 
Registered: Mar 2004
Distribution: SusE 8.2
Posts: 5,863
Blog Entries: 1

Rep: Reputation: Disabled
It's worth mentioning that Bjarne Stroustrup himself cites the Boehm GC in his latest book, Programming: Principles and Practice Using C++:

http://www.research.att.com/~bs/C++.html

IMHO .. PSM

Last edited by paulsm4; 08-30-2009 at 12:49 AM.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Java garbage collector question beev1812 Linux - Server 1 12-09-2008 11:19 AM
LXer: Pattern Analysis Tool for Java Garbage Collector LXer Syndicated Linux News 0 06-21-2008 09:00 AM
garbage collector in Java manolakis Programming 1 06-03-2008 02:20 PM
Windows garbage collector arkie Linux - General 2 03-02-2007 09:56 AM
Calling the system activity data collector NAC Fedora 1 11-10-2006 05:56 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:13 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration