Fundamentals 9 min read

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.

DaTaobao Tech
DaTaobao Tech
DaTaobao Tech
Garbage Collection Algorithms and Reference Counting in QuickJS

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.

algorithmJavaScriptMemory ManagementGarbage CollectionC++QuickJSReference Counting
DaTaobao Tech
Written by

DaTaobao Tech

Official account of DaTaobao Technology

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.