Garbage collection

  • Revision slug: SpiderMonkey/Internals/Garbage_collection
  • Revision title: Garbage collection
  • Revision id: 306171
  • Created:
  • Creator: terrence
  • Is current revision? No
  • Comment

Revision Content

About this article: This is a rough draft on SpiderMonkey GC internals. It may be out of date or inaccurate.

Design overview

SpiderMonkey has a mark-sweep garbage collection (GC) with an optionally enabled incremental marking mode. The mark phase uses a mark stack, as required by incremental marking. Sweeping of objects without finalizers is deferred to a background thread.

Work is underway to build a generational GC and a compacting GC.

Principal data structures

Cell

A Cell is the unit of memory that is allocated and collected by the GC, as used externally. In other words, from the point of view of the rest of the engine, the job of the GC is to allocate cells and automatically collect them.

Cell is the base class for all classes that are allocated by the GC, e.g., JSObject.

Allocation Kind

Cells are classified by allocation kind. The allocation kind determines the size of the object and the finalization behavior. Allocation kind is defined by enum AllocKind.

Arenas always hold objects of the same allocation kind. Thus, an arena holds objects all of the same size and finalization behavior.

Compartment

The JS heap is partitioned into compartments. Some key properties of compartments are:

  • Every cell (JS heap object) belongs to exactly one compartment. (This is equivalent to saying that the heap is partitioned into compartments.)
  • An object may not hold a direct pointer to an object in another compartment. Instead, it must store a wrapper for the other object. This allows compartments to be used for security checks: objects in the same compartment have the same access requirements, so no checks are needed, but checks may be done when traversing cross-compartment wrappers.
  • The engine may GC a single compartment without GCing the others. The engine can also GC a set of compartments without GCing the rest. Cross-compartment wrappers are used as roots for single- and multi-compartment GCs.

Compartments are a fundamental cross-cutting concept in SpiderMonkey, especially for anything related to memory, including (or especially) GC. See also Compartments.

JSCompartment contains several important GC-related fields:

ArenaLists arenas
This structure records two lists of arenas for each allocation kind: a list of free arenas and a list of allocated arenas.
bool needsBarrier
True if a GC on this compartment needs to run incremental barriers, i.e., if this compartment is currently doing an incremental GC.
CompartmentGCState gcState
Whether this compartment is currently running a GC, and if not, whether it has scheduled a GC.
size_t gcBytes, gcTriggerBytes, gcMallocBytes, gcMaxMallocBytes
Accounting data used to schedule GCs.
WrapperMap crossCompartmentWrappers
Wrappers of objects in this compartment. The map key is the object, the value is the wrapper. The mapping is required so that if a wrapper is requested for the same object multiple times, the engine can return the same wrapper every time. The set of wrapped objects is also required for single- and multi-compartment (i.e., non-global) GCs.

Chunk

A Chunk is the largest internal unit of memory allocation.

A chunk is 1MB and contains Arenas, padding, the mark bitmap (ChunkBitmap), a bitmap of decomitted arenas, and a chunk header (ChunkInfo).

The ChunkInfo contains a list of unallocated Arenas, starting at ChunkInfo::freeArenasHead and linked through ArenaHeader::next. The ChunkInfo also contains some basic stats, such as the number of free arenas.

TODO ChunkInfo next/prev

Arena

An Arena is an internal unit of memory allocation.

An Arena is one page (4096 bytes on almost all platforms) and contains an ArenaHeader, a few pad bytes, and then an array of aligned elements. All elements in an Arena have the same allocation kind and size.

Every Arena maintains a list of free spans, starting at ArenaHeader::firstFreeSpanOffets. The last cell in each free span (except the last one) holds a FreeSpan for the next free span.

Free Span

struct FreeSpan represents a contiguous sequence of free cells [first,last] within an Arena. FreeSpan contains functions to allocate from the free span.

Mark Bitmap

The mark bitmap is represented by ChunkBitmap.

The mark bitmap has one bit per GC cell. Thus, an object comprising multiple cells uses multiple bits in the bitmap.

GC roots

Note: the information here is about the implementation of GC roots and their use within SpiderMonkey.  For information on how the rooting APIs should be used by embedders, read: GC Rooting Guide

In standard CS terminology, the root set is the set of GC pointers (pointers to a GC-controlled locations) values that are immediately accessible to the program. The mark phase begins with the root set. In an exact GC system, the root set is something like the set of live GC-pointer-typed stack and global variables. In a conservative GC system, the root set contains all values stored in machine registers or stack that are addresses within GC-controlled memory regions.

The common definition as given above leaves some ambiguity about exactly what the type of a root is. Because roots are used for marking traversal, it's natural to assume that a root is a GC-pointer value, e.g., a value of type JSObject *. In SpiderMonkey, "roots" are for more than just traversal, they are also updated if the GC moves the pointed-to object. Thus, a SpiderMonkey root is more like the location of a GC-pointer value, or perhaps a GC-pointer lvalue, e.g., a JSObject ** or JSObject *&, as you like it.

Conservative scanning

Warning: SpiderMonkey has a conservative stack scanner that works just as above, but it should not be relied upon in new code. We plan to move away from the conservative scanner to an explicit rooting system in order to better support generational GC.

Explicit rooting

SpiderMonkey also has an API for registering GC roots explicitly. Moving GC (generational GC and compacting GC) will require explicit rooting, because the GC needs to know exactly which values are GC pointers so that it can update those values, and only those values, when the object moves. In the explicit rooting system, making something a GC root:

  • Notifies the GC that during marking, it should mark the pointed-to object and everything reachable from it. Failure to do so would allow the GC to collect the object, leaving the pointer dangling.
  • Allows the GC to update the pointer value if the pointed-to object is moved. Failure to do so would allow the GC to move the object without updating the pointer, again leaving the pointer dangling.
Note: In order to get moving GC, we will have to move the engine and its browser client to the explicit rooting API. Once that's done, explicit rooting is required in order not to crash, as above. However, for now we are still using the conservative stack scanner. In the current configuration, explicit rooting is only used to verify that explicit rooting is being done correctly, in order to prepare for actually using explicit rooting.

Explicit rooting API

Note: This section is a bit out of date.  js/src/gc/Root.h is a little more current and has some comments.  There is also a working draft document for the new Rooting API that may be more up to date at: https://wiki.mozilla.org/Sfink/Draft_-_GC_Pointer_Handling

Any GC pointer must be rooted during any call that may trigger a GC. There is no explicit list of calls that can trigger a GC, so any fallible API call should be considered GC-triggering. Client code roots GC pointers using the rooting API defined in GC/Root.h. The rooting classes are all template <typename T>, where T is a GC-pointer type (e.g., JSObject *) or a type that can hold a GC pointer (e.g., Value), and they root the location of a T.

The rooting classes are as follows:

Rooted
A Rooted<T> is used exactly like a T*, except that it is rooted. Thus, instead of unsafe JSObject *obj, you can safely write Rooted<JSObject> obj(cx). SpiderMonkey provides typedefs for the most frequently used types, shortening this example to RootedObject obj(cx). This is the preferred way to root local variables.

Note: Rooted<T> also provides a default constructor that does not take a JSContext*. This version roots the pointer on the runtime using the JSRuntime* stored in TLS.  The cx constructors should be prefered when performance is important.

Warning: Rooted<T> variables must be created and destroyed in a LIFO manner. As such they should not be stored in the heap or used as a temporary.

Handle
A Handle<T> represents a pointer to T that has elsewhere been registered as a root. This is primarily useful as a function parameter type. The Handle value can be assumed to be rooted, and no other explicit rooting is required. The class is set up so that a Handle can usually be constructed only from a RootedVar. There is also a static constructor method fromMarkedLocation() that creates a Handle from a raw location. This is used to make Handles for things like scope chain objects, which aren't explicitly rooted themselves, but are always reachable from the implicit stack roots.
Note: Handle does not root objects but helps maintain rooting invariants.
RawObject/RawString/Raw.*
In some cases, it is possible to statically verify that a region of code cannot GC. In these cases it is valid to use a raw pointer instead of a Rooted for efficiency. To support this usage cleanly, we provide "Raw" type names. At the moment these are just typedefs to the pointer type, however, our plan is to create a wrapper class - only enabled during debug builds - which will assert that no GC occurs during the lifetime of the pointer.

Here are the basic rules client code should follow:

  • Don't use raw JSObject * (or other gcthing pointer types, including jsval/Value) for local variables. Instead, use RootedObject, RootedValue, etc.
  • Functions that take gcthing pointers as input should declare parameters as type HandleObject or the like. JSAPI will also have parameters declared like this.
  • Functions that return gcthing pointers should return them raw, as JSObject *. But the call site should immediately store the value into a RootedVar. JSAPI also returns raw pointers like this.
  • Functions that return outparams should declare them as MutableHandleValue.  Code that sets the outparam should be changed from *out = foo to out.set(foo).
  • The general idea is that to store a gcthing pointer, use Rooted. To pass around a gcthing pointer, use Handle. Once something is a handle, it's safe, so you don't need to root it again.
  • A Handle may not outlive the Rooted it was created from.
    • If you never return Handles from a function and never store Handles in the heap, this rule will be satisfied.

Rooting verification mode

If SpiderMonkey is built with --enable-gczeal, --enable-debug, and --enable-root-analysis, then the engine will be able to run in a special mode that verifies that no stack roots are missing.  The mode can be enabled by running with GC zeal mode "6."  The root analysis mode requires a special build because it incurs both exact and conservative stack rooting overhead and is thus somewhat slower than a normal build, even when the rooting analysis is not being actively run.

Example:

# JS_GC_ZEAL=6 ./tests/jstests.py ./build/js

When a test fails in rooting verification, it will crash at JS_ASSERT(IsPoisonedPtr(p)) or because of a segfault at an address that looks like 0xdaffffff or 0x7fffdaffffff.  0xDA is the poisoned byte. These failures are easy to debug by walking up the stack and finding where the poisoned pointer was written onto the stack without proper rooting.

Marking

TODO

Incremental marking

Incremental marking means being able to do a bit of marking work, then let the mutator (JavaScript program) run a bit, then do another bit of marking work. Thus, instead of one long pause for marking, the GC does a series of short pauses. The pauses can be set to be 10ms or less.

There is always a possibility that a long pause will be required. If the allocation rate is high during incremental GC, the engine may run out of memory before finishing the incremental GC. If so, the engine must immediately restart a full, non-incremental GC in order to reclaim some memory and continue execution.

Incremental write barrier

The problem that requires a write barrier

Incremental GC requires a write barrier for correctness.

TODO diagrams for basic problem

The basic problem is as follows (see Dictionary for color terms). Say object A is black and contains a pointer field. Let object B be white. Now let the incremental slice stop, so the mutator resumes. If the mutator stores B into A, so that A contains a pointer to B, and deletes all existing pointers to B, then:

  • B is live, because A is black and contains a pointer to B.
  • B will not be marked, because B is only reachable through A and we are all done with A, because A is black.
  • Thus, B is live but will be collected.

The write barrier is a piece of code that runs just before a pointer store occurs and records just enough information to make sure that live objects don't get collected.

The SpiderMonkey incremental write barrier

SpiderMonkey uses a (relatively!) simple, common incremental write barrier called a snapshot-at-the-beginning allocate-black barrier.

To understand how this barrier works, first assume that we're going to do an incremental GC during which no new objects are allocated, just to keep things simple. How do we make sure the GC doesn't collect any live objects? One way would be to make sure that every object that was live at the beginning of the incremental GC gets marked. (This means if an object has all references to it dropped during this incremental GC, it will be collected on the next incremental GC.) This is called snapshot-at-the-beginning because it is conceptually equivalent to taking a snapshot of live objects at the beginning of the incremental GC and marking all those objects. No such snapshot is physically taken--doing so would actually require a full nonincremental mark!

The implementation of the snapshot-at-the-beginning barrier is simple. The barrier fires whenever the mutator is about to overwrite a location that holds a GC pointer. The barrier simply makes the pointed-to object black. The key observation is that the only way an object can get 'lost' and not marked is if all pointers to the object are overwitten. Thus, if whenever we're about to overwrite a pointer to an object we mark the object black first, then no objects can get 'lost'.

Now let's handle allocations correctly too. A newly allocated object wasn't present at the beginning of the GC, so the snapshot-at-the-beginning barrier doesn't do it any good. But we do need to make sure the object doesn't get collected if it's live. This is easily done by simply marking new objects immediately upon allocation during an incremental GC, thus the name allocate-black.

The SpiderMonkey incremental read barrier

The textbook version of incremental GC has only a write barrier. SpiderMonkey also needs a read barrier for weak pointers (see Dictionary).

TODO finish

Implementation details

Write barriers have a runtime cost, so SpiderMonkey tries to skip them when an incremental GC cycle is not active. Each compartment has a flag needsBarrier() that indicates whether barriers are required.

For each type T such that fields of type T* need a write barrier, there is a functionT::writeBarrierPre(old). For example, fields of type JSObject* need a write barrier, so there is a function ObjectImpl::writeBarrierPre(ObjectImpl *old). (JSObject inherits from ObjectImpl.) If compartment->needsBarrier(), then writeBarrierPre() marks old black. That's it.

The class HeapPtr<T> is provided to make it easy to invoke write barriers. A HeapPtr<T> encapsulates a T* and invokes the write barrier whenever assigned to. Thus, object fields of GC-pointer type should normally be defined as type HeapPtr<T>. The class HeapValue does the same thing for Value. HeapSlot (and the related class HeapSlotArray) is the same, but for object slots. HeapId is the same, but for jsid. TODO: why HeapValue vs HeapSlot?

Object private fields must be handled specially. The private field itself is opaque to the engine, but it may point to things that need to be marked, e.g., an array of JSObject pointers. In this example, if the private field is overwritten, the JSObject pointers could be 'lost', so a a write barrier must be invoked to mark them. ObjectImpl::privateWriteBarrierPre takes care of this by invoking the JSObject's class trace hook before the private field is set.

Another detail is that write barriers can be skipped when initializing fields of newly allocated objects. TODO explain why this is.

Sweeping

TODO

Generational GC

TODO

GC Statistics API

SpiderMonkey tracks GCs and their performance per-context using struct Statistics. There are several ways to acquire this data:

  • The environment variable MOZ_GCTIMER controls text dumping of GC stats. MOZ_GCTIMER may be none, stderr, stdout, or a filename. If a filename is given, data is appended to that file.
  • The browser preference javascript.options.mem.log controls dumping of human-readable GC stats messages to the developer console.
  • The browser preference javascript.options.mem.notify controls emission of JSON encoded GC stats to an observer interface.
JSON Format

The toplevel object contains these fields:

  • timestamp: Integer (microseconds) - Time at which the GC ended, measured from epoch.
  • total_time: Number (milliseconds) - The amount of wall time that this GC consumed.  Note: this is the sum of all the slice durations, not end time - start time.
  • compartments_collected: Integer - The number of compartments that were collected by the GC.
  • compartment_count: Integer - The number of compartments that were present in the system at the start of GC.
  • mmu_20ms: Integer (percentage) - A measurement of overall GC performance.
  • mmu_50ms: Integer (percentage) - Same algorithm, but for a longer interval.
  • scc_sweep_total: Number (milliseconds) - Like total_time, but for sweeping.
  • scc_sweep_max_pause: Number (milliseconds) - Longest duration sweep slice.
  • max_pause: Number (milliseconds) - Longest duration pause during the GC.
  • nonincremental_reason: String - A string representing the reason that an incremental GC was aborted or forced to finish in a single slice. If the incremental GC completed normally, this will be "none".
  • allocated: Integer (MB) - Number of MiB of data that were allocated by the mutator inbetween incremental slices of this GC.
  • +chunks: Integer - Number of Chunks (see above) that were acquired by the allocator for the mutator inbetween incremental slices of this GC.
  • -chunks: Integer - Number of Chunks (see above) that were released from the allocator's free chunk pool during this GC.
  • slices: List<Object> - A list of objects describing each slice in detail.
  • times: Object - A map from phase names to the total time taken by each phase: the sum of each field of the map from the slices.

Each slice object contains these fields:

  • slice: Integer - The slice index, starting at 0.
  • pause: Number (milliseconds) - The slice duration.
  • when: Number (milliseconds) - The time this slice started, relative to the first slice's start time.
  • reason: String - A string describing the API that initiated this GC slice.
  • page_faults: Integer - Number of page faults incurred during the processing of this slice.
  • start_timestamp: Integer (microseconds) - Time of slice start from epoch.
  • end_timestamp: Integer (microseconds) - Time of slice end from epoch.
  • reset (optional): String - The string passed to ResetIncrementalGC, if it was called.
  • times: Object - A map from phase names to the total time taken by that phase in this slice.

Each times object contains these fields:

  • begin_callback: Number (milliseconds)
  • wait_background_thread: Number (milliseconds)
  • purge: Number (milliseconds)
  • mark: Number (milliseconds)
  • mark_discard_code: Number (milliseconds)
  • mark_roots: Number (milliseconds)
  • mark_types: Number (milliseconds)
  • mark_delayed: Number (milliseconds)
  • mark_weak: Number (milliseconds)
  • mark_gray: Number (milliseconds)
  • mark_gray_and_weak: Number (milliseconds)
  • finalize_start_callback: Number (milliseconds)
  • sweep: Number (milliseconds)
  • sweep_atoms: Number (milliseconds)
  • sweep_compartments: Number (milliseconds)
  • sweep_tables: Number (milliseconds)
  • sweep_object: Number (milliseconds)
  • sweep_string: Number (milliseconds)
  • sweep_script: Number (milliseconds)
  • sweep_shape: Number (milliseconds)
  • sweep_discard_code: Number (milliseconds)
  • discard_analysis: Number (milliseconds)
  • discard_ti: Number (milliseconds)
  • free_ti_arena: Number (milliseconds)
  • sweep_types: Number (milliseconds)
  • clear_script_analysis: Number (milliseconds)
  • finalize_end_callback: Number (milliseconds)
  • deallocate: Number (milliseconds)
  • end_callback: Number (milliseconds)

Source files

jsgc{.h,inlines.h,.cpp}  Defines GC internal API functions, including entry points to trigger GC.

jsgcstats.{h,cpp}  Defines struct ConservativeGCStats for collecting stats about conservative stack scanning. TODO cut out when removed

gc/Barrier[-inl].h  Implements incremental and generational write barriers.

gc/Heap.h Defines Chunk, ChunkInfo, ChunkBitmap, Arena, ArenaHeader, Cell, and FreeSpan structures that define the heart of the GC heap's structures.

gc/Marking.{h,cpp} Defines all marking functions for the various garbage-collected things.

gc/Memory.{h,cpp}  Contains a few functions for mapping and unmapping pages, along with platform-specific implementations. The map/unmap functions are used by jsgc.cpp to allocate and release chunks. There are also functions to commit or decommit memory, i.e., tell the OS that certain pages are not being used and can be thrown away instead of paged to disk.

gc/Root.h  Defines classes for noting variables as GC roots.

gc/Statistics.{h,cpp}  Defines struct Statistics, which stores SpiderMonkey GC performance counters.

Dictionary of terms

TODO verify that color terms are accurate about SpiderMonkey implementation.

black  In common CS terminology, an object is black during the mark phase if it has been marked and its children are gray (have been queued for marking). An object is black after the mark phase if it has been marked. In SpiderMonkey, an object is black if its mark bit is set.

gray  In common CS terminology, an object is gray during the mark phase if it has been queued for marking. In SpiderMonkey, an object is gray if it is a child of an object in the mark stack and it is not black. Thus, gray objects are not represented explictly.

Handle  In our GC, a Handle is a pointer that has elsewhere been registered by a root.

root TODO: copy from above.

weak pointer  In common CS terminology, a weak pointer is one that doesn't keep the pointed-to value live for GC purposes. Typically, a weak pointer value has a get() method that returns a null pointer if the object has been GC'd. In Gecko/SpiderMonkey, a weak pointer is a pointer to an object that can be GC'd that is not marked through. Thus, there is no get() method and no protection against the pointed-to value getting GC'd--the programmer must ensure that the lifetime of the pointed-to object contains the lifetime of the weak pointer. TODO make sure this is correct.

white  In common CS terminology, an object is white during the mark phase if it has not been seen yet. An object is white after the mark phase if it has not been marked. In SpiderMonkey, an object is white if it is not gray or black; i.e., it is not black and it is not a child of an object in the mark stack.

Possible cleanups

MarkPagesInUse is a no-op on all platforms.

How much do we need Root? It seems like it should be used rarely.

Handle is pretty nonstandard terminology.

merge the stats files

ArenaLists::refillFreeLists seems badly named--it looks like it's trying to allocate a cell even if the arena free lists aren't full.

Revision Source

<div class="note">
  <strong>About this article: This is a rough draft on SpiderMonkey GC internals. It may be out of date or inaccurate.</strong></div>
<h2 id="Design_overview">Design overview</h2>
<p>SpiderMonkey has a mark-sweep garbage collection (GC) with an optionally enabled incremental marking mode. The mark phase uses a mark stack, as required by incremental marking. Sweeping of objects without finalizers is deferred to a background thread.</p>
<p>Work is underway to build a generational GC and a compacting GC.</p>
<h2 id="Principal_data_structures">Principal data structures</h2>
<h3 id="Cell">Cell</h3>
<p>A <strong>Cell </strong>is the unit of memory that is allocated and collected by the GC, as used externally. In other words, from the point of view of the rest of the engine, the job of the GC is to allocate cells and automatically collect them.</p>
<p>Cell is the base class for all classes that are allocated by the GC, e.g., JSObject.</p>
<h3 id="Allocation_Kind">Allocation Kind</h3>
<p>Cells are classified by allocation kind. The allocation kind determines the size of the object and the finalization behavior. Allocation kind is defined by <strong>enum AllocKind</strong>.</p>
<p>Arenas always hold objects of the same allocation kind. Thus, an arena holds objects all of the same size and finalization behavior.</p>
<h3 id="Compartment">Compartment</h3>
<p>The JS heap is partitioned into compartments. Some key properties of compartments are:</p>
<ul>
  <li>Every cell (JS heap object) belongs to exactly one compartment. (This is equivalent to saying that the heap is partitioned into compartments.)</li>
  <li>An object may not hold a direct pointer to an object in another compartment. Instead, it must store a wrapper for the other object. This allows compartments to be used for security checks: objects in the same compartment have the same access requirements, so no checks are needed, but checks may be done when traversing cross-compartment wrappers.</li>
  <li>The engine may GC a single compartment without GCing the others. The engine can also GC a set of compartments without GCing the rest. Cross-compartment wrappers are used as roots for single- and multi-compartment GCs.</li>
</ul>
<p>Compartments are a fundamental cross-cutting concept in SpiderMonkey, especially for anything related to memory, including (or especially) GC. See also <a href="/en/SpiderMonkey/SpiderMonkey_compartments" title="https://developer.mozilla.org/en/SpiderMonkey/SpiderMonkey_compartments">Compartments</a>.</p>
<p><strong>JSCompartment </strong>contains several important GC-related fields:</p>
<dl>
  <dt style="margin-left: 40px;">
    ArenaLists arenas</dt>
  <dd style="margin-left: 40px;">
    This structure records two lists of arenas for each allocation kind: a list of free arenas and a list of allocated arenas.</dd>
  <dt style="margin-left: 40px;">
    bool needsBarrier</dt>
  <dd style="margin-left: 40px;">
    True if a GC on this compartment needs to run incremental barriers, i.e., if this compartment is currently doing an incremental GC.</dd>
  <dt style="margin-left: 40px;">
    CompartmentGCState gcState</dt>
  <dd style="margin-left: 40px;">
    Whether this compartment is currently running a GC, and if not, whether it has scheduled a GC.</dd>
  <dt style="margin-left: 40px;">
    size_t gcBytes, gcTriggerBytes, gcMallocBytes, gcMaxMallocBytes</dt>
  <dd style="margin-left: 40px;">
    Accounting data used to schedule GCs.</dd>
  <dt style="margin-left: 40px;">
    WrapperMap crossCompartmentWrappers</dt>
  <dd style="margin-left: 40px;">
    Wrappers of objects in this compartment. The map key is the object, the value is the wrapper. The mapping is required so that if a wrapper is requested for the same object multiple times, the engine can return the same wrapper every time. The set of wrapped objects is also required for single- and multi-compartment (i.e., non-global) GCs.</dd>
</dl>
<h3 id="Chunk">Chunk</h3>
<p>A Chunk is the largest internal unit of memory allocation.</p>
<p>A chunk is 1MB and contains Arenas, padding, the mark bitmap (ChunkBitmap), a bitmap of decomitted arenas, and a chunk header (ChunkInfo).</p>
<p>The ChunkInfo contains a list of unallocated Arenas, starting at ChunkInfo::freeArenasHead and linked through ArenaHeader::next. The ChunkInfo also contains some basic stats, such as the number of free arenas.</p>
<p>TODO ChunkInfo next/prev</p>
<h3 id="Arena">Arena</h3>
<p>An Arena is an internal unit of memory allocation.</p>
<p>An Arena is one page (4096 bytes on almost all platforms) and contains an ArenaHeader, a few pad bytes, and then an array of aligned elements. All elements in an Arena have the same allocation kind and size.</p>
<p>Every Arena maintains a list of free spans, starting at ArenaHeader::firstFreeSpanOffets. The last cell in each free span (except the last one) holds a <strong>FreeSpan </strong>for the next free span.</p>
<h3 id="Free_Span">Free Span</h3>
<p><strong>struct FreeSpan</strong> represents a contiguous sequence of free cells <strong>[first,last]</strong> within an Arena. FreeSpan contains functions to allocate from the free span.</p>
<h3 id="Mark_Bitmap">Mark Bitmap</h3>
<p>The mark bitmap is represented by <strong>ChunkBitmap</strong>.</p>
<p>The mark bitmap has one bit per GC cell. Thus, an object comprising multiple cells uses multiple bits in the bitmap.</p>
<h2 id="GC_roots">GC roots</h2>
<div class="note">
  <p><strong>Note</strong>: the information here is about the implementation of GC roots and their use within SpiderMonkey.&nbsp; For information on how the rooting APIs should be used by embedders, read:<a href="/en-US/docs/SpiderMonkey/GC_Rooting_Guide" title="/en-US/docs/SpiderMonkey/GC_Rooting_Guide"> GC Rooting Guide</a></p>
</div>
<p>In standard CS terminology, the <strong>root set</strong> is the set of GC pointers (pointers to a GC-controlled locations) values that are immediately accessible to the program. The mark phase begins with the root set. In an exact GC system, the root set is something like the set of live GC-pointer-typed stack and global variables. In a conservative GC system, the root set contains all values stored in machine registers or stack that are addresses within GC-controlled memory regions.</p>
<p>The common definition as given above leaves some ambiguity about exactly what the type of a root is. Because roots are used for marking traversal, it's natural to assume that a root is a GC-pointer value, e.g., a value of type JSObject *. In SpiderMonkey, "roots" are for more than just traversal, they are also updated if the GC moves the pointed-to object. Thus, a SpiderMonkey root is more like the location of a GC-pointer value, or perhaps a GC-pointer lvalue, e.g., a JSObject ** or JSObject *&amp;, as you like it.</p>
<h3 id="Conservative_scanning">Conservative scanning</h3>
<div class="warning">
  <strong>Warning:</strong> SpiderMonkey has a conservative stack scanner that works just as above, but it should not be relied upon in new code. We plan to move away from the conservative scanner to an explicit rooting system in order to better support generational GC.</div>
<h3 class="warning" id="Explicit_rooting">Explicit rooting</h3>
<p>SpiderMonkey also has an API for registering GC roots explicitly. Moving GC (generational GC and compacting GC) will require explicit rooting, because the GC needs to know exactly which values are GC pointers so that it can update those values, and only those values, when the object moves. In the explicit rooting system, making something a GC root:</p>
<ul>
  <li>Notifies the GC that during marking, it should mark the pointed-to object and everything reachable from it. Failure to do so would allow the GC to collect the object, leaving the pointer dangling.</li>
  <li>Allows the GC to update the pointer value if the pointed-to object is moved. Failure to do so would allow the GC to move the object without updating the pointer, again leaving the pointer dangling.</li>
</ul>
<div class="note">
  <strong>Note:</strong> In order to get moving GC, we will have to move the engine and its browser client to the explicit rooting API. Once that's done, explicit rooting is required in order not to crash, as above. However, for now we are still using the conservative stack scanner. In the current configuration, explicit rooting is only used to verify that explicit rooting is being done correctly, in order to prepare for actually using explicit rooting.</div>
<h4 id="Explicit_rooting_API">Explicit rooting API</h4>
<div class="note">
  <strong>Note</strong>: This section is a bit out of date.&nbsp; js/src/gc/Root.h is a little more current and has some comments.&nbsp; There is also a working draft document for the new Rooting API that may be more up to date at: <a href="https://wiki.mozilla.org/Sfink/Draft_-_GC_Pointer_Handling" title="https://wiki.mozilla.org/Sfink/Draft_-_GC_Pointer_Handling">https://wiki.mozilla.org/Sfink/Draft_-_GC_Pointer_Handling</a></div>
<p>Any GC pointer must be rooted during any call that may trigger a GC. There is no explicit list of calls that can trigger a GC, so any fallible API call should be considered GC-triggering. Client code roots GC pointers using the rooting API defined in <strong>GC/Root.h</strong>. The rooting classes are all<strong> template &lt;typename T&gt;</strong>, where T is a GC-pointer type (e.g., JSObject *) or a type that can hold a GC pointer (e.g., Value), and they root the <em>location </em>of a T.</p>
<p>The rooting classes are as follows:</p>
<dl>
  <dt>
    <code>Rooted</code></dt>
  <dd>
    A <code>Rooted&lt;T&gt;</code> is used exactly like a <code>T*</code>, except that it is rooted. Thus, instead of unsafe <code>JSObject *obj</code>, you can safely write <code>Rooted&lt;JSObject&gt; obj(cx)</code>. SpiderMonkey provides typedefs for the most frequently used types, shortening this example to <code>RootedObject obj(cx)</code>. This is the preferred way to root local variables.</dd>
</dl>
<div class="note">
  <p><strong>Note:</strong> <code>Rooted&lt;T&gt;</code> also provides a default constructor that does not take a <code>JSContext*</code>. This version roots the pointer on the runtime using the <code>JSRuntime*</code> stored in TLS.&nbsp; The <code>cx</code> constructors should be prefered when performance is important.</p>
</div>
<div class="warning">
  <p><strong>Warning:</strong> Rooted&lt;T&gt; variables must be created and destroyed in a LIFO manner. As such they should not be stored in the heap or used as a temporary.</p>
</div>
<dl>
  <dt>
    <code>Handle</code></dt>
  <dd>
    A <code>Handle&lt;T&gt;</code> represents a pointer to <code>T</code> that has elsewhere been registered as a root. This is primarily useful as a function parameter type. The <code>Handle</code> value can be assumed to be rooted, and no other explicit rooting is required. The class is set up so that a <code>Handle</code> can usually be constructed only from a <code>RootedVar</code>. There is also a static constructor method <code>fromMarkedLocation()</code> that creates a <code>Handle</code> from a raw location. This is used to make Handles for things like scope chain objects, which aren't explicitly rooted themselves, but are always reachable from the implicit stack roots.</dd>
</dl>
<div class="note">
  <strong>Note</strong>: <code>Handle</code> does not root objects but helps maintain rooting invariants.</div>
<dl>
  <dt>
    <code>RawObject/RawString/Raw.*</code></dt>
  <dd>
    In some cases, it is possible to statically verify that a region of code cannot GC. In these cases it is valid to use a raw pointer instead of a <code>Rooted</code> for efficiency. To support this usage cleanly, we provide "Raw" type names. At the moment these are just typedefs to the pointer type, however, our plan is to create a wrapper class - only enabled during debug builds - which will assert that no GC occurs during the lifetime of the pointer.</dd>
</dl>
<p class="note">Here are the basic rules client code should follow:</p>
<ul>
  <li class="note">Don't use raw <code>JSObject *</code> (or other <code>gcthing</code> pointer types, including <code>jsval/Value</code>) for local variables. Instead, use <code>RootedObject,</code> <code>RootedValue</code>, etc.</li>
  <li class="note">Functions that take <code>gcthing</code> pointers as input should declare parameters as type <code>HandleObject</code> or the like. JSAPI will also have parameters declared like this.</li>
  <li class="note">Functions that return <code>gcthing</code> pointers should return them raw, as <code>JSObject *</code>. But the call site should immediately store the value into a <code>RootedVar</code>. JSAPI also returns raw pointers like this.</li>
  <li class="note">Functions that return outparams should declare them as <code>MutableHandleValue</code>.&nbsp; Code that sets the outparam should be changed from <code>*out = foo</code> to <code>out.set(foo)</code>.</li>
  <li class="note">The general idea is that to store a <code>gcthing</code> pointer, use <code>Rooted</code>. To pass around a <code>gcthing</code> pointer, use <code>Handle</code>. Once something is a handle, it's safe, so you don't need to root it again.</li>
  <li class="note">A <code>Handle</code> may not outlive the <code>Rooted</code> it was created from.
    <ul>
      <li class="note">If you never return <code>Handle</code>s from a function and never store <code>Handle</code>s in the heap, this rule will be satisfied.</li>
    </ul>
  </li>
</ul>
<h4 id="Rooting_verification_mode">Rooting verification mode</h4>
<p>If SpiderMonkey is built with --enable-gczeal, --enable-debug, and --enable-root-analysis, then the engine will be able to run in a special mode that verifies that no stack roots are missing.&nbsp; The mode can be enabled by running with GC zeal mode "6."&nbsp; The root analysis mode requires a special build because it incurs both exact and conservative stack rooting overhead and is thus somewhat slower than a normal build, even when the rooting analysis is not being actively run.</p>
<div class="note">
  <p>Example:</p>
  <p># JS_GC_ZEAL=6 ./tests/jstests.py ./build/js</p>
</div>
<p>When a test fails in rooting verification, it will crash at JS_ASSERT(IsPoisonedPtr(p)) or because of a segfault at an address that looks like 0xdaffffff or 0x7fffdaffffff.&nbsp; 0xDA is the poisoned byte. These failures are easy to debug by walking up the stack and finding where the poisoned pointer was written onto the stack without proper rooting.</p>
<h2 id="Marking">Marking</h2>
<p>TODO</p>
<h2 id="Incremental_marking">Incremental marking</h2>
<p>Incremental marking means being able to do a bit of marking work, then let the mutator (JavaScript program) run a bit, then do another bit of marking work. Thus, instead of one long pause for marking, the GC does a series of short pauses. The pauses can be set to be 10ms or less.</p>
<p>There is always a possibility that a long pause will be required. If the allocation rate is high during incremental GC, the engine may run out of memory before finishing the incremental GC. If so, the engine must immediately restart a full, non-incremental GC in order to reclaim some memory and continue execution.</p>
<h3 id="Incremental_write_barrier">Incremental write barrier</h3>
<h4 id="The_problem_that_requires_a_write_barrier">The problem that requires a write barrier</h4>
<p>Incremental GC requires a write barrier for correctness.</p>
<p>TODO diagrams for basic problem</p>
<p>The basic problem is as follows (see Dictionary for color terms). Say object A is black and contains a pointer field. Let object B be white. Now let the incremental slice stop, so the mutator resumes. If the mutator stores B into A, so that A contains a pointer to B, and deletes all existing pointers to B, then:</p>
<ul>
  <li>B is live, because A is black and contains a pointer to B.</li>
  <li>B will not be marked, because B is only reachable through A and we are all done with A, because A is black.</li>
  <li>Thus, B is live but will be collected.</li>
</ul>
<p>The write barrier is a piece of code that runs just before a pointer store occurs and records just enough information to make sure that live objects don't get collected.</p>
<h4 id="The_SpiderMonkey_incremental_write_barrier">The SpiderMonkey incremental write barrier</h4>
<p>SpiderMonkey uses a (relatively!) simple, common incremental write barrier called a <strong>snapshot-at-the-beginning allocate-black barrier</strong>.</p>
<p>To understand how this barrier works, first assume that we're going to do an incremental GC during which no new objects are allocated, just to keep things simple. How do we make sure the GC doesn't collect any live objects? One way would be to make sure that every object that was live at the beginning of the incremental GC gets marked. (This means if an object has all references to it dropped during this incremental GC, it will be collected on the <em>next </em>incremental GC.) This is called <strong>snapshot-at-the-beginning</strong> because it is <em>conceptually</em> equivalent to taking a snapshot of live objects at the beginning of the incremental GC and marking all those objects. No such snapshot is physically taken--doing so would actually require a full nonincremental mark!</p>
<p style="margin-left: ;">The implementation of the snapshot-at-the-beginning barrier is simple. The barrier fires whenever the mutator is about to overwrite a location that holds a GC pointer. The barrier simply makes the pointed-to object black. The key observation is that the only way an object can get 'lost' and not marked is if all pointers to the object are overwitten. Thus, if whenever we're about to overwrite a pointer to an object we mark the object black first, then no objects can get 'lost'.</p>
<p>Now let's handle allocations correctly too. A newly allocated object wasn't present at the beginning of the GC, so the snapshot-at-the-beginning barrier doesn't do it any good. But we do need to make sure the object doesn't get collected if it's live. This is easily done by simply marking new objects immediately upon allocation during an incremental GC, thus the name <strong>allocate-black</strong>.</p>
<h4 id="The_SpiderMonkey_incremental_read_barrier">The SpiderMonkey incremental read barrier</h4>
<p>The textbook version of incremental GC has only a write barrier. SpiderMonkey also needs a read barrier for weak pointers (see Dictionary).</p>
<p>TODO finish</p>
<h4 id="Implementation_details">Implementation details</h4>
<p>Write barriers have a runtime cost, so SpiderMonkey tries to skip them when an incremental GC cycle is not active. Each compartment has a flag <code>needsBarrier()</code> that indicates whether barriers are required.</p>
<p>For each type <code>T</code> such that fields of type <code>T*</code> need a write barrier, there is a function<code>T::writeBarrierPre(old)</code>. For example, fields of type <code>JSObject*</code> need a write barrier, so there is a function <code>ObjectImpl::writeBarrierPre(ObjectImpl *old)</code>. (<code>JSObject</code> inherits from <code>ObjectImpl</code>.) If <code><strong>compartment-&gt;needsBarrier()</strong></code>, then <code>writeBarrierPre()</code> marks <code>old </code>black. That's it.</p>
<p>The class <code>HeapPtr&lt;T&gt;</code> is provided to make it easy to invoke write barriers. A <code>HeapPtr&lt;T&gt;</code> encapsulates a <strong>T*</strong> and invokes the write barrier whenever assigned to. Thus, object fields of GC-pointer type should normally be defined as type HeapPtr&lt;T&gt;. The class <strong>HeapValue </strong>does the same thing for Value. <strong>HeapSlot </strong>(and the related class <strong>HeapSlotArray</strong>) is the same, but for object slots. <strong>HeapId </strong>is the same, but for jsid. TODO: why HeapValue vs HeapSlot?</p>
<p>Object private fields must be handled specially. The private field itself is opaque to the engine, but it may point to things that need to be marked, e.g., an array of JSObject pointers. In this example, if the private field is overwritten, the JSObject pointers could be 'lost', so a a write barrier must be invoked to mark them. <strong>ObjectImpl::privateWriteBarrierPre</strong> takes care of this by invoking the JSObject's class trace hook before the private field is set.</p>
<p>Another detail is that write barriers can be skipped when initializing fields of newly allocated objects. TODO explain why this is.</p>
<h2 id="Sweeping">Sweeping</h2>
<p>TODO</p>
<h2 id="Generational_GC">Generational GC</h2>
<p>TODO</p>
<h2 id="GC_statistics">GC Statistics API</h2>
<p>SpiderMonkey tracks GCs and their performance per-context using <strong>struct Statistics</strong>. There are several ways to acquire this data:</p>
<ul>
  <li>The environment variable <strong>MOZ_GCTIMER </strong>controls text dumping of GC stats. <strong>MOZ_GCTIMER</strong> may be <strong>none</strong>, <strong>stderr</strong>, <strong>stdout</strong>, or a filename. If a filename is given, data is appended to that file.</li>
  <li>The browser preference<strong> javascript.options.mem.log</strong> controls dumping of human-readable GC stats messages to the developer console.</li>
  <li>The browser preference <strong>javascript.options.mem.notify</strong> controls emission of JSON encoded GC stats to an observer interface.</li>
</ul>
<h5 id="JSON_Format">JSON Format</h5>
<p>The toplevel object contains these fields:</p>
<ul>
  <li><strong>timestamp</strong>: <code>Integer</code> (microseconds) - Time at which the GC ended, measured from epoch.</li>
  <li><strong>total_time</strong>: <code>Number</code> (milliseconds) - The amount of wall time that this GC consumed.&nbsp; Note: this is the sum of all the slice durations, not end time - start time.</li>
  <li><strong>compartments_collected</strong>: <code>Integer</code> - The number of compartments that were collected by the GC.</li>
  <li><strong>compartment_count</strong>: <code>Integer</code> - The number of compartments that were present in the system at the start of GC.</li>
  <li><strong>mmu_20ms</strong>: <code>Integer</code> (percentage) - A measurement of overall GC performance.</li>
  <li><strong>mmu_50ms</strong>: <code>Integer</code> (percentage) - Same algorithm, but for a longer interval.</li>
  <li><strong>scc_sweep_total</strong>: <code>Number</code> (milliseconds) - Like total_time, but for sweeping.</li>
  <li><strong>scc_sweep_max_pause</strong>: <code>Number</code> (milliseconds) - Longest duration sweep slice.</li>
  <li><strong>max_pause</strong>: <code>Number</code> (milliseconds) - Longest duration pause during the GC.</li>
  <li><strong>nonincremental_reason</strong>: <code>String</code> - A string representing the reason that an incremental GC was aborted or forced to finish in a single slice. If the incremental GC completed normally, this will be "none".</li>
  <li><strong>allocated</strong>: <code>Integer</code> (MB) - Number of MiB of data that were allocated by the mutator inbetween incremental slices of this GC.</li>
  <li><strong>+chunks</strong>: <code>Integer</code> - Number of Chunks (see above) that were acquired by the allocator for the mutator inbetween incremental slices of this GC.</li>
  <li><strong>-chunks</strong>: <code>Integer</code> - Number of Chunks (see above) that were released from the allocator's free chunk pool during this GC.</li>
  <li><strong>slices</strong>: <code>List&lt;Object&gt;</code> - A list of objects describing each slice in detail.</li>
  <li><strong>times</strong>: <code>Object</code> - A map from phase names to the total time taken by each phase: the sum of each field of the map from the slices.</li>
</ul>
<p>Each <strong>slice</strong> object contains these fields:</p>
<ul>
  <li><strong>slice</strong>: <code>Integer</code> - The slice index, starting at 0.</li>
  <li><strong>pause</strong>: <code>Number</code> (milliseconds) - The slice duration.</li>
  <li><strong>when</strong>: <code>Number</code> (milliseconds) - The time this slice started, relative to the first slice's start time.</li>
  <li><strong>reason</strong>: <code>String</code> - A string describing the API that initiated this GC slice.</li>
  <li><strong>page_faults</strong>: <code>Integer</code> - Number of page faults incurred during the processing of this slice.</li>
  <li><strong>start_timestamp</strong>: <code>Integer</code> (microseconds) - Time of slice start from epoch.</li>
  <li><strong>end_timestamp</strong>: <code>Integer</code> (microseconds) - Time of slice end from epoch.</li>
  <li><strong>reset<em> (optional)</em></strong>: <code>String</code> - The string passed to ResetIncrementalGC, if it was called.</li>
  <li><strong>times</strong>: <code>Object</code> - A map from phase names to the total time taken by that phase in this slice.</li>
</ul>
<p>Each <strong>times</strong> object contains these fields:</p>
<ul>
  <li><strong>begin_callback</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>wait_background_thread</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>purge</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark_discard_code</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark_roots</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark_types</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark_delayed</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark_weak</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark_gray</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>mark_gray_and_weak</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>finalize_start_callback</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_atoms</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_compartments</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_tables</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_object</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_string</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_script</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_shape</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_discard_code</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>discard_analysis</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>discard_ti</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>free_ti_arena</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>sweep_types</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>clear_script_analysis</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>finalize_end_callback</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>deallocate</strong>: <code>Number</code> (milliseconds)</li>
  <li><strong>end_callback</strong>: <code>Number</code> (milliseconds)</li>
</ul>
<h2 id="Source_files">Source files</h2>
<p><strong>jsgc{.h,inlines.h,.cpp}&nbsp; </strong>Defines GC internal API functions, including entry points to trigger GC.</p>
<p><strong>jsgcstats.{h,cpp}</strong>&nbsp; Defines <strong>struct ConservativeGCStats</strong> for collecting stats about conservative stack scanning. TODO cut out when removed</p>
<p><strong>gc/Barrier[-inl].h</strong>&nbsp; Implements incremental and generational write barriers.</p>
<p><strong>gc/Heap.h</strong> Defines <code>Chunk</code>, <code>ChunkInfo</code>, <code>ChunkBitmap</code>, <code>Arena</code>, <code>ArenaHeader</code>, <code>Cell</code>, and <code>FreeSpan</code> structures that define the heart of the GC heap's structures.</p>
<p><strong>gc/Marking.{h,cpp}</strong> Defines all marking functions for the various garbage-collected things.</p>
<p><strong>gc/Memory.{h,cpp}</strong>&nbsp; Contains a few functions for mapping and unmapping pages, along with platform-specific implementations. The map/unmap functions are used by jsgc.cpp to allocate and release chunks. There are also functions to commit or decommit memory, i.e., tell the OS that certain pages are not being used and can be thrown away instead of paged to disk.</p>
<p><strong>gc/Root.h</strong>&nbsp; Defines classes for noting variables as GC roots.</p>
<p><strong>gc/Statistics.{h,cpp}</strong>&nbsp; Defines <strong>struct Statistics</strong>, which stores SpiderMonkey GC performance counters.</p>
<h2 id="Dictionary_of_terms">Dictionary of terms</h2>
<p>TODO verify that color terms are accurate about SpiderMonkey implementation.</p>
<p><strong>black&nbsp; </strong>In common CS terminology, an object is black during the mark phase if it has been marked and its children are gray (have been queued for marking). An object is black after the mark phase if it has been marked. In SpiderMonkey, an object is black if its mark bit is set.</p>
<p><strong>gray&nbsp; </strong>In common CS terminology, an object is gray during the mark phase if it has been queued for marking. In SpiderMonkey, an object is gray if it is a child of an object in the mark stack and it is not black. Thus, gray objects are not represented explictly.</p>
<p><strong>Handle</strong>&nbsp; In our GC, a <strong>Handle </strong>is a pointer that has elsewhere been registered by a root.</p>
<p><strong>root </strong>TODO: copy from above.</p>
<p><strong>weak pointer&nbsp;</strong> In common CS terminology, a weak pointer is one that doesn't keep the pointed-to value live for GC purposes. Typically, a weak pointer value has a get() method that returns a null pointer if the object has been GC'd. In Gecko/SpiderMonkey, a weak pointer is a pointer to an object that can be GC'd that is not marked through. Thus, there is no get() method and no protection against the pointed-to value getting GC'd--the programmer must ensure that the lifetime of the pointed-to object contains the lifetime of the weak pointer. TODO make sure this is correct.</p>
<p><strong>white&nbsp; </strong>In common CS terminology, an object is white during the mark phase if it has not been seen yet. An object is white after the mark phase if it has not been marked. In SpiderMonkey, an object is white if it is not gray or black; i.e., it is not black and it is not a child of an object in the mark stack.</p>
<h2 id="Possible_cleanups">Possible cleanups</h2>
<p><strong>MarkPagesInUse </strong>is a no-op on all platforms.</p>
<p>How much do we need <strong>Root</strong>? It seems like it should be used rarely.</p>
<p><strong>Handle </strong>is pretty nonstandard terminology.</p>
<p>merge the stats files</p>
<p>ArenaLists::refillFreeLists seems badly named--it looks like it's trying to allocate a cell even if the arena free lists aren't full.</p>
Revert to this revision