Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/quickjs-ng/quickjs/llms.txt

Use this file to discover all available pages before exploring further.

Garbage Collection

QuickJS uses automatic garbage collection with configurable thresholds. These functions allow you to control and trigger garbage collection.

JS_RunGC

Manually triggers garbage collection.
void JS_RunGC(JSRuntime *rt);

Parameters

rt
JSRuntime *
required
The runtime to run garbage collection on.

Description

Immediately runs a garbage collection cycle on the runtime. This will:
  • Mark all reachable objects
  • Free all unreachable objects
  • Call finalizers for freed objects
  • Reclaim memory
Garbage collection normally runs automatically when memory allocation crosses the GC threshold. Manual triggering is useful for:
  • Freeing memory at strategic points (e.g., between requests)
  • Testing and debugging memory leaks
  • Ensuring cleanup before measuring memory usage
  • Controlling GC timing in performance-critical code
Garbage collection is already automatic. You typically don’t need to call this function unless you have specific timing or memory requirements.

Example

#include <quickjs.h>

int main() {
    JSRuntime *rt = JS_NewRuntime();
    JSContext *ctx = JS_NewContext(rt);
    
    // Run some JavaScript that creates temporary objects
    JS_Eval(ctx, "for (let i = 0; i < 1000; i++) { let x = {data: new Array(100)}; }",
            62, "<input>", JS_EVAL_TYPE_GLOBAL);
    
    // Manually trigger GC to free temporary objects
    JS_RunGC(rt);
    
    // Check memory usage after GC
    JSMemoryUsage stats;
    JS_ComputeMemoryUsage(rt, &stats);
    printf("Memory after GC: %lld bytes\n", (long long)stats.malloc_size);
    
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

Example: Periodic GC

// Run GC periodically in a server application
#include <quickjs.h>
#include <time.h>

typedef struct {
    JSRuntime *rt;
    time_t last_gc;
    int requests_since_gc;
} ServerContext;

void handle_request(ServerContext *server, const char *script) {
    JSContext *ctx = JS_NewContext(server->rt);
    
    // Execute request
    JS_Eval(ctx, script, strlen(script), "request", JS_EVAL_TYPE_GLOBAL);
    
    JS_FreeContext(ctx);
    server->requests_since_gc++;
    
    // Run GC every 100 requests or every 60 seconds
    time_t now = time(NULL);
    if (server->requests_since_gc >= 100 || 
        (now - server->last_gc) >= 60) {
        JS_RunGC(server->rt);
        server->last_gc = now;
        server->requests_since_gc = 0;
    }
}

Example: Debug Memory Leaks

// Use GC to detect memory leaks
void check_for_leaks(JSRuntime *rt) {
    // Enable leak detection
    JS_SetDumpFlags(rt, JS_DUMP_LEAKS | JS_DUMP_GC);
    
    // Run GC to collect unreferenced objects
    JS_RunGC(rt);
    
    // GC will print leaked objects to stdout
}

JS_SetGCThreshold

Sets the garbage collection threshold.
void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold);

Parameters

rt
JSRuntime *
required
The runtime to configure.
gc_threshold
size_t
required
Number of bytes to allocate before triggering automatic garbage collection.

Description

Controls how frequently automatic garbage collection runs. When total allocated memory increases by gc_threshold bytes since the last GC, a new GC cycle is triggered. Lower threshold:
  • More frequent GC cycles
  • Lower peak memory usage
  • Higher CPU overhead
  • More predictable memory usage
Higher threshold:
  • Less frequent GC cycles
  • Higher peak memory usage
  • Lower CPU overhead
  • Better performance for allocation-heavy code
The default GC threshold is automatically set based on runtime characteristics. You typically only need to adjust this for performance tuning or memory-constrained environments.

Example

#include <quickjs.h>

int main() {
    JSRuntime *rt = JS_NewRuntime();
    
    // Set GC to run every 256 KB of allocations
    JS_SetGCThreshold(rt, 256 * 1024);
    
    JSContext *ctx = JS_NewContext(rt);
    
    // This will trigger multiple GC cycles
    const char *code = 
        "let arrays = [];\n"
        "for (let i = 0; i < 100; i++) {\n"
        "  arrays.push(new Array(10000));\n"  // Allocates memory
        "}";
    
    JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_GLOBAL);
    
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

Example: Memory-Constrained Device

// Configure for embedded system with limited memory
JSRuntime *create_embedded_runtime(void) {
    JSRuntime *rt = JS_NewRuntime();
    
    // Set low memory limit
    JS_SetMemoryLimit(rt, 2 * 1024 * 1024);  // 2 MB max
    
    // Set aggressive GC threshold (run GC every 128 KB)
    JS_SetGCThreshold(rt, 128 * 1024);
    
    return rt;
}

Example: Performance Tuning

// Configure for performance-critical application
JSRuntime *create_performance_runtime(void) {
    JSRuntime *rt = JS_NewRuntime();
    
    // Set high memory limit
    JS_SetMemoryLimit(rt, 512 * 1024 * 1024);  // 512 MB max
    
    // Set relaxed GC threshold (run GC every 10 MB)
    JS_SetGCThreshold(rt, 10 * 1024 * 1024);
    
    return rt;
}

JS_GetGCThreshold

Retrieves the current garbage collection threshold.
size_t JS_GetGCThreshold(JSRuntime *rt);

Parameters

rt
JSRuntime *
required
The runtime to query.

Returns

threshold
size_t
The current GC threshold in bytes.

Description

Returns the number of bytes that must be allocated before automatic garbage collection triggers.

Example

#include <quickjs.h>

void print_gc_config(JSRuntime *rt) {
    size_t threshold = JS_GetGCThreshold(rt);
    printf("GC threshold: %zu bytes (%.2f MB)\n",
           threshold, threshold / 1024.0 / 1024.0);
}

int main() {
    JSRuntime *rt = JS_NewRuntime();
    
    // Check default threshold
    print_gc_config(rt);
    
    // Modify threshold
    JS_SetGCThreshold(rt, 512 * 1024);
    
    // Verify new threshold
    print_gc_config(rt);
    
    JS_FreeRuntime(rt);
    return 0;
}

Example: Adaptive GC

// Adjust GC threshold based on memory pressure
void adjust_gc_threshold(JSRuntime *rt) {
    JSMemoryUsage stats;
    JS_ComputeMemoryUsage(rt, &stats);
    
    size_t current_threshold = JS_GetGCThreshold(rt);
    
    // If we're using > 80% of our memory limit, make GC more aggressive
    if (stats.malloc_size > stats.malloc_limit * 0.8) {
        size_t new_threshold = current_threshold / 2;
        JS_SetGCThreshold(rt, new_threshold);
        printf("Memory pressure high, reducing GC threshold to %zu\n",
               new_threshold);
    }
    // If we're using < 50% of our limit, relax GC frequency
    else if (stats.malloc_size < stats.malloc_limit * 0.5) {
        size_t new_threshold = current_threshold * 2;
        JS_SetGCThreshold(rt, new_threshold);
        printf("Memory pressure low, increasing GC threshold to %zu\n",
               new_threshold);
    }
}

Example: GC Statistics

// Track GC behavior over time
typedef struct {
    size_t gc_count;
    size_t total_freed;
    double avg_gc_time;
} GCStats;

void log_gc_stats(JSRuntime *rt, GCStats *stats) {
    printf("\nGC Statistics:\n");
    printf("  Threshold: %zu bytes\n", JS_GetGCThreshold(rt));
    printf("  Total GCs: %zu\n", stats->gc_count);
    printf("  Avg GC time: %.2f ms\n", stats->avg_gc_time);
    printf("  Total freed: %zu bytes\n", stats->total_freed);
}

Best Practices

When to Use Manual GC

// Good: Between independent tasks
void process_batch(JSRuntime *rt, const char **scripts, int count) {
    for (int i = 0; i < count; i++) {
        JSContext *ctx = JS_NewContext(rt);
        JS_Eval(ctx, scripts[i], strlen(scripts[i]), "script", 
                JS_EVAL_TYPE_GLOBAL);
        JS_FreeContext(ctx);
    }
    
    // Clean up after batch
    JS_RunGC(rt);
}

// Bad: Too frequent manual GC
void process_items(JSRuntime *rt, int count) {
    for (int i = 0; i < count; i++) {
        // Process item...
        JS_RunGC(rt);  // Don't do this! Too frequent.
    }
}

Memory-Constrained Configuration

// Complete setup for embedded/constrained environment
JSRuntime *create_constrained_runtime(void) {
    JSRuntime *rt = JS_NewRuntime();
    
    // Strict memory limit
    JS_SetMemoryLimit(rt, 4 * 1024 * 1024);  // 4 MB
    
    // Aggressive GC
    JS_SetGCThreshold(rt, 256 * 1024);  // 256 KB
    
    // Enable GC debugging
    JS_SetDumpFlags(rt, JS_DUMP_GC | JS_DUMP_GC_FREE);
    
    return rt;
}
Excessive manual GC calls can hurt performance. Let the automatic GC do its job unless you have specific requirements. Only call JS_RunGC() at strategic points like between requests or after freeing large data structures.