CITS 3242 Programming Paradigms Part IV: Advanced Topics Topic 19: Implementation Garbage Collection Most languages in the functional, logic, and object-oriented paradigms include some form of automatic memory management (including garbage collection). This topic introduces the main concepts involved and the main types of collectors. 1
Garbage Collection Garbage collection is a common term for automatic storage management. Storage management involves allocating memory that a program can use to store data. It also involves reclaiming and reusing this memory when the program no longer needs it technically this part is garbage collection (GC). Traditionally in imperative languages like C and C++, the program must explicitly request memory and later indicate when it is no longer needed. Explicit requests would not fit well with functional/logic languages so these have generally always included automatic management. This allows the programmer to focus on the values involved in their program, without having to worry about low-level details like storage. This also avoids the need to debug a whole class of errors - dangling pointers, overwritten values, access violations. Early GC techniques were inefficient, but recent techniques can match and often beat manual allocation. Mainstream OO languages now also exploit the advantages of GC. Manual allocation is still sometimes necessary for low-level programs. (like building a garbage collector!). 2
Cells and the reachable set Each value is represented in as a block of memory known as a cell A cell contains a tag and one or more fields The tag indicates the type of value (int, array, etc.) and/or the constructor (:: or [] for lists, None, Some, etc.) Each field contains an atomic value (a number) or a pointer (address) The reachable set is all the cells that a program could access in future. This includes the directly accessible roots, which are the CPU registers and the stack of variables for function calls that still need to return. The set also includes every cell pointed to by another one, i.e., all cells that can be reached from the roots by following pointers. During the execution of a program, each cell is in one of three states free, i.e. it has not been allocated food, i.e. it has been allocated and it is holding data that is in use by the program garbage, i.e. it has been allocated, but the data that it holds is no longer in use The free cells are known collectively as the free space of the machine The food cells make up the program graph 3
Storage management tasks The storage manager has two principal tasks The system provides cells to hold the values in new data structures the storage manager must allocate cells from the free-space this process is called storage allocation The system collects garbage cells and adds them to the free-space so that they can be used again the storage manager must find all the garbage cells and arrange them in a data structure this process is called garbage collection Note that the total amount of memory that a program can allocate is unbounded, although the amount that may be food at any given instant is limited by the size of the machine 4
Types of garbage collection algorithms Reference-counting garbage collection The system keeps track of the number of references to each individual cell: when the count for a cell drops to zero, the cell is garbage and is reclaimed garbage cells are reclaimed immediately Tracing garbage collection The system allocates free-space until it is exhausted, then 'saves' ('traces') the food cells and reclaims the rest garbage cells are reclaimed periodically tracing can be achieved in either of two ways: Mark-scan garbage collection The food cells are marked: all unmarked cells are garbage cells and are reclaimed one at a time Copying garbage collection The food cells are moved to a free part of store: the old part of store is now entirely garbage and is reclaimed all at once 5
Comparing garbage collectors Time and space overheads less time spent garbage collecting is better less space for tags, reference counts, etc is better, and generally also avoids the time to update these things. Reference patterns 'tight' reference patterns are better for caching and virtual memory performance many gc's incorporate storage compaction Effect on reaction time the user's program shouldn't be stopped for a long time especially real-time reactive programs An interesting example: reference counting can lead to delays when large data structures become inaccessible. Tracing collectors can generally be done incrementally instead. Scope for parallelism a parallel algorithm should distribute well over several processors 6
Copying garbage collection The store is split into two halves only one half is in use at a given time we call the half in use the current space and the half in reserve the next space the next space is all free note that the next space need not always be in real memory The system allocates cells in the current space until it is full the program is halted and the garbage collector is started (Note: allocation just requires checking and moving a pointer.) The garbage collector copies the program graph (the food cells) from the current space into the next space the current space is now all garbage and can be made free The garbage collector is halted and the program is restarted in the next space 7
Performance of copying garbage collection Time complexity is linear in the number of food cells particularly good when most cells are garbage Space overheads are tolerable need twice as much memory during copying Reference patterns are tight compaction comes for free Allocation is very fast just test and modify the a pointer to the next free word. The effect on reaction time can be severe the program is stopped for the whole garbage collection incremental copying algorithms are available that interleave garbage collection with program execution The scope for parallelism (in incremental algorithms) is good 8
Generational collection Generational collectors use copying collection, but instead of two halves, the store is split into generations. Allocation is performed in the smallest generation, called the nursery. When a generation fills, all live cells in it are copied to the next generation, which is generally somewhat larger. Typical modern generational collectors have 2-3 generations. The oldest (and largest) generation can be collected using any of the gc techniques. Mark and sweep and copying collection are common. Generational collectors quickly filter out long lived data from short-lived garbage, without copying all food cells on every collection. A disadvantage is they require a write-barrier which must record every time an old generation cell is mutated to point to a new generation cell. In practice this cost is usually not a major concern, although sometimes choosing immutable data structures may improve performance. 9
Incremental and parallel collection Garbage collection can lead to pauses while tracing/copying food cells. Many modern collectors use incremental techniques to minimize these, by alternating between running the program and tracing/copying. This idea can be extended to parallel collection, which allows the gc to be running on a separate thread/core/cpu. This reduces pauses, but slightly increases overhead. For servers, often non-incremental collection is preferred. For interactive systems that may regularly be waiting for user input, incremental or parallel collection works particularly well. Current mainstream collectors (CLR, JVM) tend to be generational, with the option of parallel collection for interactive use. 10