Memory Management & Reference Counting
Relevant source files
The following files were used as context for generating this wiki page:
libfastjson implements a reference counting object model to manage the lifecycle of JSON objects. This model allows for shared ownership of objects across different parts of an application, ensuring that memory is only deallocated when the last reference to an object is released.
The library provides a set of core functions to increment and decrement reference counts, alongside specialized constructors for different JSON types (objects, arrays, strings, etc.). By using reference counting, libfastjson simplifies memory management for complex, nested JSON structures where multiple parent objects might point to the same child node.
Sources: README.html:15-18, json_object.h:134-142
Reference Counting Mechanism
The core of libfastjson's memory management is the fjson_object structure, which contains a reference count field (_ref_count). When an object is created, its reference count is initialized to 1.
Ownership Rules
- Creation: Functions like
fjson_object_new_object()orfjson_object_new_string()return an object with a reference count of 1. The caller is the initial owner. - Transfer: When adding an object to an array (via
fjson_object_array_add) or an object (viafjson_object_object_add), ownership transfers to the container. The reference count is not incremented by these functions. - Shared Ownership: To maintain a reference to an object after adding it to a container, the caller must call
fjson_object_get(). - Release: Every reference acquired via
neworgetmust eventually be released viafjson_object_put().
Sources: json_object.h:325-333, json_object.h:444-449, json_object.c:265-274
Atomic Operations
To support multi-threaded environments, libfastjson uses atomic operations for manipulating reference counts. These are defined in atomic.h and wrap GCC atomic builtins or provide mutex-backed fallbacks.
This diagram illustrates the lifecycle of an object based on its reference count.
Sources: atomic.h:39-50, json_object.c:254-263
Key Functions and Data Structures
Primary Reference Functions
| Function | Description | Impact on Ref Count |
|---|---|---|
fjson_object_get | Increments the reference count to grab shared ownership. | +1 |
fjson_object_put | Decrements the reference count and frees the object if it reaches 0. | -1 |
Sources: json_object.h:145-161, json_object.c:254-263
Internal Deletion Logic
Each fjson_object contains a function pointer _delete that points to the appropriate cleanup routine for that specific type. For example:
- Strings: Frees the internal string buffer if it was dynamically allocated.
- Arrays: Calls
array_list_free, which in turn puts all contained objects. - Objects: Iterates through all child pages and puts all child objects.
Sources: json_object.c:451-466, json_object.c:859-863, json_object.c:980-984
Memory Allocation in Specialized Structures
String Memory Optimization
libfastjson employs an optimization for short strings (defined by LEN_DIRECT_STRING_DATA). Small strings are stored directly within the object structure to avoid a separate malloc and free cycle, while larger strings use strdup.
Sources: json_object.c:877-889, json_object.c:134-138
Print Buffer Management
The printbuf structure is used for string serialization. It features a dynamic resizing mechanism that doubles the buffer size when capacity is reached.
The sequence shows how the print buffer manages its internal heap memory during serialization.
Sources: printbuf.c:45-58, printbuf.c:68-96
Object Child Pagination
To manage key-value pairs in JSON objects efficiently, libfastjson uses a paged structure (_fjson_child_pg). This avoids frequent large reallocations for objects with many members. Each page holds a fixed number of children (FJSON_OBJECT_CHLD_PG_SIZE).
Sources: json_object.h:43-50, json_object.c:451-460
Implementation Example: Object Creation and Release
The following logic demonstrates the standard pattern for creating a complex object and managing its memory.
struct fjson_object *my_obj = fjson_object_new_object(); // Ref count: 1
struct fjson_object *my_str = fjson_object_new_string("value"); // Ref count: 1
// Transfers ownership of my_str to my_obj
fjson_object_object_add(my_obj, "key", my_str);
// Release my_obj. This will internally call put on my_str
fjson_object_put(my_obj);
Sources: tests/test1.c:127-133, json_object.c:560-575
Conclusion
Memory management in libfastjson is deterministic through the use of reference counting. By delegating cleanup to type-specific _delete functions and utilizing atomic operations, the library provides a robust framework for handling JSON data structures in C. Developers must be diligent in following ownership transfer rules—specifically that adding an object to a container consumes the reference—to prevent memory leaks or double-free errors.
Sources: README.html:15-18, json_object.h:325-333