comparison src/gc.c @ 5238:2cc24c69446c

Document the new allocator and the new garbage collector in gc.c and mc-alloc.c.
author Marcus Crestani <crestani@informatik.uni-tuebingen.de>
date Mon, 05 Jul 2010 18:17:39 +0200
parents 71ee43b8a74d
children 308d34e9f07d
comparison
equal deleted inserted replaced
5237:6466bc9ebf15 5238:2cc24c69446c
18 along with XEmacs; see the file COPYING. If not, write to 18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */ 20 Boston, MA 02111-1307, USA. */
21 21
22 /* Synched up with: Not in FSF. */ 22 /* Synched up with: Not in FSF. */
23
24 /*
25 Garbage Collectors in XEmacs
26
27 Currently, XEmacs comes with two garbage collectors:
28
29 - The "old garbage collector": a simple mark and sweep collector,
30 its implementation is mainly spread out over gc.c and alloc.c.
31 It is used by the default configuration or if you configure
32 `--with-newgc=no'.
33
34 - The "new garbage collector": an incremental mark and sweep collector,
35 its implementation is in gc.c. It is used if you configure
36 `--with-newgc'. It comes with a new allocator, see mc-alloc.c, and
37 with the KKCC mark algorith, see below.
38
39 Additionally, the old garbage collectors comes with two mark algorithms:
40
41 - The "recursive mark algorithm" marks live objects by recursively
42 calling mark_* functions on live objects. It is the default mark
43 algorithm of the old garbage collector.
44
45 - The "KKCC mark algorithm" uses an explicit stack that to keep
46 track of the current progress of traversal and uses memory layout
47 descriptions (that are also used by the portable dumper) instead
48 of the mark_* functions. The old garbage collector uses it if
49 you configure `--with-kkcc'. It is the default and only mark
50 algorithm of the new garbage collector.
51
52
53 The New Incremental Garbage Collector
54
55 An incremental garbage collector keeps garbage collection pause
56 times short by interleaving small amounts of collection work with
57 program execution, it does that by instrumenting write barrier
58 algorithms that essentially allow interrupting the mark phase.
59
60
61 Write Barrier
62
63 A write barrier is the most important prerequisite for fancy
64 garbage collection techniques. We implement a "Virtual Dirty Bit
65 (short: vdb) Write Barrier" that makes uses of the operating
66 system's memory-protection mechanisms: The write barrier
67 write-protects memory pages containing heap objects. If the
68 mutator tries to modify these objects by writing into the
69 write-protected page, the operating system generates a fault. The
70 write barrier catches this fault, reads out the error-causing
71 address and can thus identify the updated object and page.
72
73 Not all environments and operating systems provide the mechanism to
74 write-protect memory, catch resulting write faults, and read out
75 the faulting address. But luckily, most of today's operating
76 systems provide the features needed for the write-barrier
77 implementation. Currently, XEmacs includes write-barrier
78 implementations for the following platforms:
79
80 - POSIX-compliant platforms like up-to-date UNIX, Linux, Solaris,
81 etc. use the system call `mprotect' for memory protection,
82 `sigaction' for signal handling and get the faulting address from
83 `struct siginfo'. See file vdb-posix.c.
84
85 - Mach-based systems like Mac OS X use "Mach Exception Handlers".
86 See file vdb-mach.c.
87
88 - Windows systems like native Windows and Cygwin use Microsoft's
89 so-called "Structured Exception Handling". See file vdb-win32.c.
90
91 The configure script determines which write barrier implementation
92 to use for a system. If no write barrier implementation is working
93 on that system, a fall-back "fake" implementation is used: This
94 implementation simply turns of the incremental write barrier at
95 runtime and does not allow any incremental collection (see
96 vdb-fake.c). The garbage collector then acts like a traditional
97 mark-and-sweep garbage collector. Generally, the incremental
98 garbage collector can be turned of at runtime by the user or by
99 applications, see below.
100
101
102 Memory Protection and Object Layout
103
104 Implementations of a memory-protection mechanism may restrict the
105 size and the alignment of the memory region to be on page-size
106 boundaries. All objects subject to be covered by the write barrier
107 have to be allocated on logical memory pages, so that they meet the
108 requirement to be write-protected. The new allocator mc-alloc is
109 aware of a system page size---it allocates all Lisp objects on
110 logical memory pages and is therefore defaulted to on when the new
111 garbage collector is enabled.
112
113 Unfortunately, the Lisp object layout that works with the old
114 collector leads to holes in the write barrier: Not all data
115 structures containing pointers to Lisp objects are allocated on the
116 Lisp heap. Some Lisp objects do not carry all their information in
117 the object itself. External parts are kept in separately allocated
118 memory blocks that are not managed by the new Lisp allocator.
119 Examples for these objects are hash tables and dynamic arrays, two
120 objects that can dynamically grow and shrink. The separate memory
121 blocks are not guaranteed to reside on page boundaries, and thus
122 cannot be watched by the write barrier.
123
124 Moreover, the separate parts can contain live pointers to other Lisp
125 objects. These pointers are not covered by the write barrier and
126 modifications by the client during garbage collection do escape. In
127 this case, the client changes the connectivity of the reachability
128 graph behind the collector's back, which eventually leads to
129 erroneous collection of live objects. To solve this problem, I
130 transformed the separately allocated parts to fully qualified Lisp
131 objects that are managed by the allocator and thus are covered by
132 the write barrier. This also removes a lot of special allocation
133 and removal code for the out-sourced parts. Generally, allocating
134 all data structures that contain pointers to Lisp objects on one
135 heap makes the whole memory layout more consistent.
136
137
138 Debugging
139
140 The virtual-dirty-bit write barrier provokes signals on purpose,
141 namely SIGSEGV and SIGBUS. When debugging XEmacs with this write
142 barrier running, the debugger always breaks whenever a signal
143 occurs. This behavior is generally desired: A debugger has to break
144 on signals, to allow the user to examine the cause of the
145 signal---especially for illegal memory access, which is a common
146 programming error. But the debugger should not break for signals
147 caused by the write barrier. Therefore, most debuggers provide the
148 ability to turn of their fault handling for specific signals. The
149 configure script generates the debugger's settings .gdbinit and
150 .dbxrc, adding code to turn of signal handling for SIGSEGV and
151 SIGBUS, if the new garbage collector is used.
152
153 But what happens if a bug in XEmacs causes an illegal memory access?
154 To maintain basic debugging abilities, we use another signal: First,
155 the write-barrier signal handler has to determine if the current
156 error situation is caused by the write-barrier memory protection or
157 not. Therefore, the signal handler checks if the faulting address
158 has been write-protected before. If it has not, the fault is caused
159 by a bug; the debugger has to break in this situation. To achieve
160 this, the signal handler raises SIGABRT to abort the program. Since
161 SIGABRT is not masked out by the debugger, XEmacs aborts and allows
162 the user to examine the problem.
163
164
165 Incremental Garbage Collection
166
167 The new garbage collector is still a mark-and-sweep collector, but
168 now the mark phase no longer runs in one atomic action, it is
169 interleaved with program execution. The incremental garbage
170 collector needs an explicit mark stack to store the state of the
171 incremental traversal: the KKCC mark algorithm is a prerequisite and
172 is enabled by default when the new garbage collector is on.
173
174 Garbage collection is invoked as before: After `gc-cons-threshold'
175 bytes have been allocated since the last garbage collection (or
176 after `gc-cons-percentage' percentage of the total amount of memory
177 used for Lisp data has been allocated since the last garbage
178 collection) a collection starts. After some initialization, the
179 marking begins.
180
181 The variable `gc-incremental-traversal-threshold' contains how many
182 steps of incremental work have to be executed in one incremental
183 traversal cycle. After that many steps have been made, the mark
184 phase is interrupted and the client resumes. Now, the Lisp memory
185 is write-protected and the write barrier records modified objects.
186 Incremental traversal is resumed after
187 `gc-cons-incremental-threshold' bytes have been allocated since the
188 interruption of garbage collection. Then, the objects recorded by
189 the write-barrier have to be re-examined by the traversal, i.e. they
190 are re-pushed onto the mark stack and processed again. Once the
191 mark stack is empty, the traversal is done.
192
193 A full incremental collection is slightly slower than a full garbage
194 collection before: There is an overhead for storing pointers into
195 objects when the write barrier is running, and an overhead for
196 repeated traversal of modified objects. However, the new
197 incremental garbage collector reduces client pause times to
198 one-third, so even when a garbage collection is running, XEmacs
199 stays reactive.
200
201
202 Tricolor Marking: White, Black, and Grey Mark Bits
203
204 Garbage collection traverses the graph of reachable objects and
205 colors them. The objects subject to garbage collection are white at
206 the beginning. By the end of the collection, those that will be
207 retained are colored black. When there are no reachable objects left
208 to blacken, the traversal of live data structures is finished. In
209 traditional mark-and-sweep collectors, this black and white coloring
210 is sufficient.
211
212 In an incremental collector, the intermediate state of the traversal
213 is im- portant because of ongoing mutator activity: the mutator
214 cannot be allowed to change things in such way that the collector
215 will fail to find all reachable objects. To understand and prevent
216 such interactions between the mutator and the collector, it is
217 useful to introduce a third color, grey.
218
219 Grey objects have been reached by the traversal, but its descendants
220 may not have been. White objects are changed to grey when they are
221 reached by the traversal. Grey objects mark the current state of the
222 traversal: traversal pro- ceeds by processing the grey objects. The
223 KKCC mark stack holds all the currently grey-colored objects.
224 Processing a grey object means following its outgoing pointers, and
225 coloring it black afterwards.
226
227 Intuitively, the traversal proceeds in a wavefront of grey objects
228 that separates the unreached objects, which are colored white, from
229 the already processed black objects.
230
231 The allocator takes care of storing the mark bits: The mark bits are
232 kept in a tree like structure, for details see mc-alloc.c.
233
234
235 Internal States of the Incremental Garbage Collector
236
237 To keep track of its current state, the collector holds it's current
238 phase in the global `gc_state' variable. A collector phase is one
239 of the following:
240
241 NONE No incremental or full collection is currently running.
242
243 INIT_GC The collector prepares for a new collection, e.g. sets some
244 global variables.
245
246 PUSH_ROOT_SET The collector pushes the root set on the mark stack
247 to start the traversal of live objects.
248
249 MARK The traversal of live objects colors the reachable objects
250 white, grey, or black, according to their lifeness. The mark
251 phase can be interrupted by the incremental collection algorithm:
252 Before the client (i.e. the non collector part of XEmacs) resumes,
253 the write barrier has to be installed so that the collector knows
254 what objects get modified during the collector's pause.
255 Installing a write barrier means protecting pages that only
256 contain black objects and recording write access to these objects.
257 Pages with white or grey objects do not need to be protected,
258 since these pages are due to marking anyways when the collector
259 resumes. Once the collector resumes, it has to re-scan all
260 objects that have been modified during the collector pause and
261 have been caught by the write barrier. The mark phase is done when
262 there are no more grey objects on the heap, i.e. the KKCC mark stack
263 is empty.
264
265 REPUSH_ROOT_SET After the mark phase is done, the collector has to
266 traverse the root set pointers again, since modifications to the
267 objects in the root set can not all be covered by the write barrier
268 (e.g. root set objects that are on the call stack). Therefore, the
269 collector has to traverse the root set again without interruption.
270
271 FINISH_MARK After the mark phase is finished, some objects with
272 special liveness semantics have to be treated separately, e.g.
273 ephemerons and the various flavors of weak objects.
274
275 FINALIZE The collector registers all objects that have finalizers
276 for finalization. Finalizations happens asynchronously sometimes
277 after the collection has finished.
278
279 SWEEP The allocator scans the entire heap and frees all white marked
280 objects. The freed memory is recycled and can be re-used for future
281 allocations. The sweep phase is carried out atomically.
282
283 FINISH_GC The collector cleans up after the garbage collection by
284 resetting some global variables.
285
286
287 Lisp Interface
288
289 The new garbage collector can be accessed directly from Emacs Lisp.
290 Basically, two functions invoke the garbage collector:
291
292 (gc-full) starts a full garbage collection. If an incremental
293 garbage collection is already running, it is finished without
294 further interruption. This function guarantees that unused
295 objects have been freed when it returns.
296
297 (gc-incremental) starts an incremental garbage collection. If an
298 incremental garbage collection is already running, the next cycle
299 of incremental traversal is started. The garbage collection is
300 finished if the traversal completes. Note that this function does
301 not necessarily free any memory. It only guarantees that the
302 traversal of the heap makes progress.
303
304 The old garbage collector uses the function (garbage-collect) to
305 invoke a garbage collection. This function is still in use by some
306 applications that explicitly want to invoke a garbage collection.
307 Since these applications may expect that unused memory has really
308 been freed when (garbage-collect) returns, it maps to (gc-full).
309
310 The new garbage collector is highly customizable during runtime; it
311 can even be switched back to the traditional mark-and-sweep garbage
312 collector: The variable allow-incremental-gc controls whether
313 garbage collections may be interrupted or if they have to be carried
314 out in one atomic action. Setting allow-incremental-gc to nil
315 prevents incremental garbage collection, and the garbage collector
316 then only does full collects, even if (gc-incremental) is called.
317 Non-nil allows incremental garbage collection.
318
319 This way applications can freely decide what garbage collection
320 algorithm is best for the upcoming memory usage. How frequently a
321 garbage collection occurs and how much traversal work is done in one
322 incremental cycle can also be modified during runtime. See
323
324 M-x customize RET alloc RET
325
326 for an overview of all settings.
327
328
329 More Information
330
331 More details can be found in
332 http://crestani.de/xemacs/pdf/thesis-newgc.pdf .
333
334 */
23 335
24 #include <config.h> 336 #include <config.h>
25 #include "lisp.h" 337 #include "lisp.h"
26 338
27 #include "backtrace.h" 339 #include "backtrace.h"
48 #include "sysdep.h" 360 #include "sysdep.h"
49 #include "window.h" 361 #include "window.h"
50 #include "vdb.h" 362 #include "vdb.h"
51 363
52 364
365 /* Number of bytes of consing since gc before a full gc should happen. */
53 #define GC_CONS_THRESHOLD 2000000 366 #define GC_CONS_THRESHOLD 2000000
367
368 /* Number of bytes of consing since gc before another cycle of the gc
369 should happen in incremental mode. */
54 #define GC_CONS_INCREMENTAL_THRESHOLD 200000 370 #define GC_CONS_INCREMENTAL_THRESHOLD 200000
371
372 /* Number of elements marked in one cycle of incremental GC. */
55 #define GC_INCREMENTAL_TRAVERSAL_THRESHOLD 100000 373 #define GC_INCREMENTAL_TRAVERSAL_THRESHOLD 100000
56 374
57 /* Number of bytes of consing done since the last GC. */ 375 /* Number of bytes of consing done since the last GC. */
58 EMACS_INT consing_since_gc; 376 EMACS_INT consing_since_gc;
59 377