Garbage Collection Algorithms and Reference Counting in QuickJS
QuickJS manages memory using reference counting for each object combined with a cycle‑collector that periodically scans roots, decrements child references, and frees objects whose counts drop to zero, while also supporting traditional reachability‑based garbage‑collection techniques such as mark‑sweep, copying, and generational collection.
Memory management is familiar to C/C++ developers (malloc/free, new/delete) but higher‑level languages like Java and JavaScript rely on automatic memory management, known as garbage collection (GC).
GC originated in the 1950s and works by identifying unreachable objects (garbage) and reclaiming their memory. Understanding GC helps improve code efficiency.
Common GC Algorithms Overview
GC performs two main tasks: find garbage and recycle memory.
To solve these, GC must define what is garbage and how to reclaim it efficiently.
What is Garbage
Garbage is memory that cannot be accessed by any other object. Two approaches:
Reference Counting (RC)
Reachability Analysis
Reference Counting
Each object header has a counter ref_count . Increment on new references, decrement on releases; when it reaches zero, the object can be reclaimed.
struct JSGCObjectHeader {
int ref_count; /* must come first, 32-bit */
JSGCObjectTypeEnum gc_obj_type : 4;
uint8_t mark : 4; /* used by the GC */
uint8_t dummy1;
uint16_t dummy2;
struct list_head link;
};Example usage:
p->header.ref_count = 1; static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) {
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
p->ref_count++;
}
return (JSValue)v;
} static inline void JS_FreeValue(JSContext *ctx, JSValue v) {
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
if (--p->ref_count <= 0) {
__JS_FreeValue(ctx, v);
}
}
}Reachability Analysis
GC Roots (stack variables, globals, etc.) are starting points. Objects reachable from roots are considered alive; others are garbage.
How to Reclaim
Mark‑Sweep
Copying Collector
Mark‑Compact
Generational Collector
Mark‑Sweep marks reachable objects then frees unmarked ones.
Copying collector divides the heap into two equal spaces (from/to), copies live objects to the to‑space, then swaps.
Mark‑Compact adds a compaction phase after sweeping to reduce fragmentation.
Generational collector treats most objects as short‑lived (young generation) and promotes survivors to the old generation.
QuickJS GC
QuickJS uses reference counting with a cycle‑collector. All allocated JS objects are linked in gc_obj_list . When memory pressure triggers, QuickJS runs:
void JS_RunGC(JSRuntime *rt) {
/* decrement the reference of the children of each object. mark = 1 after this pass. */
gc_decref(rt);
/* keep the GC objects with a non zero refcount and their childs */
gc_scan(rt);
/* free the GC objects in a cycle */
gc_free_cycles(rt);
}The cycle collector marks objects, decrements child references, moves objects with zero refcount to a temporary list, then frees them, effectively breaking reference cycles.
DaTaobao Tech
Official account of DaTaobao Technology
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.