Skip to main content

Memory Management

QuickJS provides flexible memory management options including memory limits and custom allocators.

JS_SetMemoryLimit

Sets a memory allocation limit for the runtime.
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);

Parameters

rt
JSRuntime *
required
The runtime to configure.
limit
size_t
required
Maximum number of bytes the runtime can allocate. Use 0 to disable the memory limit.

Description

Sets a hard limit on memory allocation for the runtime. When the limit is reached:
  • Further allocations will fail
  • JavaScript code will throw out-of-memory errors
  • The runtime remains in a valid state
This is essential for:
  • Preventing runaway scripts from consuming all system memory
  • Running untrusted code safely
  • Implementing resource quotas in multi-tenant systems
  • Ensuring predictable memory usage
Setting the limit to 0 disables memory limiting. The runtime will be able to allocate memory up to system limits.

Example

#include <quickjs.h>

int main() {
    JSRuntime *rt = JS_NewRuntime();
    
    // Set a 10 MB memory limit
    JS_SetMemoryLimit(rt, 10 * 1024 * 1024);
    
    JSContext *ctx = JS_NewContext(rt);
    
    // This script will fail if it tries to allocate > 10 MB
    const char *code = 
        "let arr = [];\n"
        "for (let i = 0; i < 1000000; i++) {\n"
        "  arr.push(new Array(1000));\n"  // Will hit memory limit
        "}";
    
    JSValue result = JS_Eval(ctx, code, strlen(code), "<input>", 
                             JS_EVAL_TYPE_GLOBAL);
    
    if (JS_IsException(result)) {
        // Handle out-of-memory error
        JSValue exception = JS_GetException(ctx);
        const char *error = JS_ToCString(ctx, exception);
        printf("Error: %s\n", error);  // "out of memory"
        JS_FreeCString(ctx, error);
        JS_FreeValue(ctx, exception);
    }
    
    JS_FreeValue(ctx, result);
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

Monitoring Memory Usage

#include <quickjs.h>

typedef struct {
    int64_t malloc_size;
    int64_t malloc_limit;
    int64_t memory_used_size;
} JSMemoryUsage;

void print_memory_usage(JSRuntime *rt) {
    JSMemoryUsage stats;
    JS_ComputeMemoryUsage(rt, &stats);
    
    printf("Memory used: %lld / %lld bytes\n",
           (long long)stats.malloc_size,
           (long long)stats.malloc_limit);
}

int main() {
    JSRuntime *rt = JS_NewRuntime();
    JS_SetMemoryLimit(rt, 50 * 1024 * 1024);  // 50 MB limit
    
    JSContext *ctx = JS_NewContext(rt);
    
    // Check memory before running script
    print_memory_usage(rt);
    
    JS_Eval(ctx, "let x = new Array(10000);", 26, "<input>",
            JS_EVAL_TYPE_GLOBAL);
    
    // Check memory after running script
    print_memory_usage(rt);
    
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

Dynamic Limits

// Adjust memory limit based on available system memory
#include <sys/sysinfo.h>

void set_adaptive_memory_limit(JSRuntime *rt) {
    struct sysinfo info;
    sysinfo(&info);
    
    // Use 25% of available RAM
    size_t limit = info.freeram / 4;
    JS_SetMemoryLimit(rt, limit);
    
    printf("Set memory limit to %zu MB\n", limit / 1024 / 1024);
}

Custom Allocators

For advanced use cases, you can provide custom memory allocators when creating a runtime with JS_NewRuntime2().

JSMallocFunctions Structure

typedef struct JSMallocFunctions {
    void *(*js_calloc)(void *opaque, size_t count, size_t size);
    void *(*js_malloc)(void *opaque, size_t size);
    void (*js_free)(void *opaque, void *ptr);
    void *(*js_realloc)(void *opaque, void *ptr, size_t size);
    size_t (*js_malloc_usable_size)(const void *ptr);
} JSMallocFunctions;

Allocator Functions

js_malloc
void *(*)(void *opaque, size_t size)
required
Allocates size bytes. Returns pointer to allocated memory or NULL on failure.
js_calloc
void *(*)(void *opaque, size_t count, size_t size)
required
Allocates memory for an array of count elements of size bytes each, initialized to zero.
js_free
void (*)(void *opaque, void *ptr)
required
Frees previously allocated memory.
js_realloc
void *(*)(void *opaque, void *ptr, size_t size)
required
Resizes the memory block pointed to by ptr to size bytes.
js_malloc_usable_size
size_t (*)(const void *ptr)
required
Returns the usable size of the allocated memory block. Return 0 if not supported.

Example: Tracking Allocator

#include <quickjs.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct {
    size_t total_allocated;
    size_t peak_allocated;
    size_t allocation_count;
    size_t free_count;
} AllocStats;

static void *tracked_malloc(void *opaque, size_t size) {
    AllocStats *stats = opaque;
    void *ptr = malloc(size + sizeof(size_t));
    
    if (ptr) {
        *(size_t *)ptr = size;
        stats->total_allocated += size;
        stats->allocation_count++;
        
        if (stats->total_allocated > stats->peak_allocated) {
            stats->peak_allocated = stats->total_allocated;
        }
        
        return (char *)ptr + sizeof(size_t);
    }
    return NULL;
}

static void tracked_free(void *opaque, void *ptr) {
    if (!ptr) return;
    
    AllocStats *stats = opaque;
    void *real_ptr = (char *)ptr - sizeof(size_t);
    size_t size = *(size_t *)real_ptr;
    
    stats->total_allocated -= size;
    stats->free_count++;
    free(real_ptr);
}

static void *tracked_realloc(void *opaque, void *ptr, size_t size) {
    if (!ptr) return tracked_malloc(opaque, size);
    if (size == 0) {
        tracked_free(opaque, ptr);
        return NULL;
    }
    
    AllocStats *stats = opaque;
    void *real_ptr = (char *)ptr - sizeof(size_t);
    size_t old_size = *(size_t *)real_ptr;
    
    void *new_ptr = realloc(real_ptr, size + sizeof(size_t));
    if (new_ptr) {
        *(size_t *)new_ptr = size;
        stats->total_allocated = stats->total_allocated - old_size + size;
        
        if (stats->total_allocated > stats->peak_allocated) {
            stats->peak_allocated = stats->total_allocated;
        }
        
        return (char *)new_ptr + sizeof(size_t);
    }
    return NULL;
}

static void *tracked_calloc(void *opaque, size_t count, size_t size) {
    size_t total = count * size;
    void *ptr = tracked_malloc(opaque, total);
    if (ptr) memset(ptr, 0, total);
    return ptr;
}

static size_t tracked_malloc_usable_size(const void *ptr) {
    return 0;  // Not implemented
}

int main() {
    AllocStats stats = {0};
    
    JSMallocFunctions mf = {
        .js_malloc = tracked_malloc,
        .js_calloc = tracked_calloc,
        .js_free = tracked_free,
        .js_realloc = tracked_realloc,
        .js_malloc_usable_size = tracked_malloc_usable_size
    };
    
    JSRuntime *rt = JS_NewRuntime2(&mf, &stats);
    JSContext *ctx = JS_NewContext(rt);
    
    // Run some JavaScript
    JS_Eval(ctx, "let arr = new Array(10000);", 26, "<input>",
            JS_EVAL_TYPE_GLOBAL);
    
    printf("Allocations: %zu\n", stats.allocation_count);
    printf("Frees: %zu\n", stats.free_count);
    printf("Current usage: %zu bytes\n", stats.total_allocated);
    printf("Peak usage: %zu bytes\n", stats.peak_allocated);
    
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    
    printf("\nAfter cleanup:\n");
    printf("Leaked: %zu bytes\n", stats.total_allocated);
    
    return 0;
}

Example: Pool Allocator

// Simple pool allocator for small objects
#define POOL_SIZE 1024
#define MAX_SMALL_ALLOC 64

typedef struct {
    char pool[POOL_SIZE];
    size_t offset;
    size_t small_allocs;
} PoolAllocator;

static void *pool_malloc(void *opaque, size_t size) {
    PoolAllocator *pool = opaque;
    
    // Use pool for small allocations
    if (size <= MAX_SMALL_ALLOC && 
        pool->offset + size <= POOL_SIZE) {
        void *ptr = &pool->pool[pool->offset];
        pool->offset += size;
        pool->small_allocs++;
        return ptr;
    }
    
    // Fall back to malloc for large allocations
    return malloc(size);
}

// Note: This is a simplified example. A real pool allocator
// would need proper tracking to distinguish pool vs malloc'd pointers
When implementing custom allocators:
  • All functions must be thread-safe if using multiple runtimes
  • js_malloc_usable_size is optional - return 0 if not implemented
  • Ensure proper alignment of allocated memory
  • Handle NULL parameters correctly in js_free and js_realloc

Build docs developers (and LLMs) love