Introduction
JSON.stringify is a core JavaScript function that serializes objects into JSON strings. Its performance directly impacts everything from network requests to localStorage operations. A faster JSON.stringify means quicker page interactions and more responsive applications. The V8 team recently achieved a more than twofold speed improvement for this critical function through targeted optimizations. This article explores the technical changes that made this leap possible.
The Foundation: A Side-Effect-Free Fast Path
The cornerstone of this optimization is a new fast path built on a simple premise: if V8 can guarantee that serializing an object will not trigger any side effects—such as executing user code or triggering garbage collection—then it can use a highly specialized, streamlined implementation.
Traditionally, the general-purpose serializer must constantly check for potential side effects, adding overhead. By detecting that an object and its values are plain data (no custom toJSON methods, no getters, no circular references that require special handling), V8 can bypass those expensive checks and stick to the fast path. This yields significant speedups for the most common use cases: serializing plain JavaScript objects and arrays.
Moreover, the new fast path is iterative rather than recursive. This architectural choice eliminates stack overflow checks and allows the serializer to quickly resume if a large nested structure is encountered. As a result, developers can now serialize much deeper object graphs without performance penalties. For more details on which objects qualify for the fast path, see Limitations.
String Representation Optimizations
Strings in V8 can be stored using either one-byte or two-byte characters. Pure ASCII strings are stored as one-byte (1 byte per character), while strings containing non-ASCII characters use two-byte (2 bytes per character). The serializer formerly used a unified code path that constantly branched on the character type, wasting cycles.
To eliminate this overhead, V8 now templatizes the stringifier based on the string's character width. Two distinct, optimized versions of the serializer are compiled—one for one-byte strings, another for two-byte strings. While this increases binary size slightly, the performance gains far outweigh the cost. Fewer conditional checks per character translate directly into faster serialization.
Handling Mixed Encodings Efficiently
Real-world objects often contain strings of both types. The serializer inspects each string's instance type to detect representations that cannot be handled on the fast path (e.g., ConsString, which might trigger a garbage collection during flattening). If such a string is encountered, V8 falls back to the slower general-purpose path. This check is necessary and performed during serialization, but the vast majority of strings fall into the one-byte or two-byte categories that the fast path handles directly.
Conclusion
By introducing a side-effect-free fast path and specializing the string serializer for one- and two-byte strings, V8 has made JSON.stringify more than twice as fast. These optimizations are transparent to developers—no API changes required—but offer immediate performance benefits for any application that serializes data. Whether you are sending JSON over the network or caching data locally, your JavaScript just got a little faster.
These improvements are part of our ongoing work to make V8 more efficient.