Skip to main content

Reference Counting

QuickJS uses reference counting for memory management of JavaScript values. Understanding and correctly using these functions is critical to prevent memory leaks and use-after-free bugs.

Overview

JSValue represents a JavaScript value which can be a primitive type or an object. Values with reference counts (objects, strings, etc.) must be explicitly managed:
  • Increment the reference count when you want to keep a value: JS_DupValue()
  • Decrement the reference count when done with a value: JS_FreeValue()
Primitive values like numbers, booleans, null, and undefined don’t have reference counts and don’t need to be freed, but calling JS_FreeValue() on them is safe and does nothing.

Reference Counting Rules

The QuickJS API follows these conventions:

Function Parameters

  • JSValue parameter: Function takes ownership; caller must NOT call JS_FreeValue()
  • JSValueConst parameter: Function does NOT take ownership; caller MUST call JS_FreeValue() later

Return Values

  • Function returning JSValue: Transfers ownership to caller; caller MUST call JS_FreeValue()
  • Function returning JSValueConst: Does NOT transfer ownership; caller must NOT call JS_FreeValue()

Core Functions

JS_FreeValue

Decrements the reference count of a value and frees it if the count reaches zero.
Parameters:
  • ctx - JavaScript context
  • v - Value to free
Example:
Note: It is safe to call this on values without reference counts (numbers, booleans, etc.). It is NOT safe to call this on uninitialized values.

JS_FreeValueRT

Decrements the reference count using the runtime instead of a context.
Parameters:
  • rt - JavaScript runtime
  • v - Value to free
Use Case: Used in finalizers and other situations where you have the runtime but not a context. Example:

JS_DupValue

Increments the reference count of a value.
Parameters:
  • ctx - JavaScript context
  • v - Value to duplicate
Returns: The same value with incremented reference count Example:

JS_DupValueRT

Increments the reference count using the runtime.
Parameters:
  • rt - JavaScript runtime
  • v - Value to duplicate
Returns: The same value with incremented reference count

Common Patterns

Creating and Freeing Values

Passing Values to Functions

When a function takes JSValue (not JSValueConst), it takes ownership:
When a function takes JSValueConst, it does NOT take ownership:

Returning Values from C Functions

C functions that return JSValue must return a newly allocated value:

Storing Values

When storing a value in a data structure, duplicate it:

Error Handling

When an error occurs, free any values you’ve created:

Using goto for Cleanup

A common pattern for complex initialization:

Reference Count Checks

JS_VALUE_HAS_REF_COUNT

Checks if a value has a reference count.
Example:

Debugging Reference Counting Issues

QuickJS provides dump flags to help debug memory issues:
Enable these before JS_FreeRuntime() to see what wasn’t properly freed.

Common Mistakes

Double Free

Memory Leak

Using After Free

Not Duplicating Stored Values

Best Practices

  1. Always match creation with destruction: Every JS_New* should have a corresponding JS_FreeValue
  2. Check function signatures: JSValue parameters take ownership, JSValueConst don’t
  3. Duplicate when storing: Use JS_DupValue() when storing a value in a data structure
  4. Use goto for cleanup: In complex functions, use goto to ensure proper cleanup on error
  5. Free in reverse order: When freeing multiple values, free them in reverse order of creation
  6. Check for exceptions: Always check return values for JS_EXCEPTION before using them
  7. Use the build-time checker: Compile with -DJS_CHECK_JSVALUE during development to catch type mismatches