Huge changes in ArduinoJson 6.6.0
14 November 2018
EDIT: I had to roll back the changes from this version, see the article
ArduinoJson 6.6.0-beta has just been released. It introduced a radical change in the design of the library: the allocator is not monotonic anymore, it can also release memory.
How older versions worked
In ArduinoJson version 5, JsonBuffer
implemented a custom allocator to use the RAM efficiently.
To keep the code small and reduce the memory footprint small, I implemented the simplest form of allocator: a monotonic allocator.
A monotonic allocator can allocate memory, but it cannot release it. That’s why functions like JsonObject::remove()
leak memory.
This following program illustrates the problem (online demo):
DynamicJsonBuffer jb;
JsonObject& root = jb.parseObject("{\"wifi\":{\"ssid\":\"hello\"},\"host\":\"myproject\"}");
Serial.println(jb.size());
root.remove("wifi");
Serial.println(jb.size()); // same value
Because the allocator is not able to release the memory in the JsonBuffer
, the value returned by size()
doesn’t change.
With version 6, JsonDocument
replaced JsonBuffer
, but the allocator remained the same.
How the new version works
Now that microcontrollers have become more powerful, we can afford a complete allocator in JsonDocument
.
Starting with ArduinoJson 6.6.0-beta, the allocator is now able to release and compact the memory inside the JsonDocument
.
Now, functions that remove or update elements do not leak anymore.
This following program highlight the new feature (online demo):
DynamicJsonDocument doc;
deserializeJson(doc, "{\"wifi\":{\"ssid\":\"hello\"},\"host\":\"myproject\"}");
Serial.println(doc.memoryUsage());
doc.as<JsonObject>().remove("wifi");
Serial.println(doc.memoryUsage()); // much lower
With this version, the value of memoryUsage()
decreases after calling JsonObject::remove()
, showing that the allocator properly releases all the memory associated with the removed value.
How much does it cost?
Of course, switch from a lightweight monotonic allocator to a full-fledged allocator doesn’t come for free.
The size code of the code increased, and the memory footprint is slightly higher. The tables below show the results for the two programs above.
**On an Arduino UNO **:
Version | Program size | Before remove | After remove |
---|---|---|---|
5.13.3 | 4424 | 69 | 69 |
6.5.0 | 6350 | 64 | 64 |
6.6.0 | 8642 | 94 | 38 |
On an ESP8266:
Version | Program size | Before remove | After remove |
---|---|---|---|
5.13.3 | 251588 | 100 | 100 |
6.5.0 | 253212 | 110 | 110 |
6.6.0 | 254636 | 163 | 63 |
Before jumping to conclusions, remember that version 5.13.3 has been highly optimized over the years, so it’s normal if it’s much more efficient. Of course, I’ll do my best to improve the future revisions.
Also, remember that ArduinoJson 6 will never be as efficient as version 5. When I designed version 5, most users had Arduino UNO s, so I had to optimize the efficiency, sacrificing the ease-of-use. Nowadays, most users have ESP8266, so I’m moving the cursor toward the ease-of-use side.
Is that all?
In this new version of ArduinoJson, I removed several redundant functions:
JsonArray::is<T>(i)
JsonObject::is<T>(k)
JsonArray::set(i,v)
JsonObject::set(k,v)
And I also replaced the two following function:
T JsonArray::get<T>(i)
becameJsonVariant JsonArray::get(i)
T JsonObject::get<T>(k)
becameJsonVariant JsonObject::get(k)
Conclusion
This new allocation strategy should enable new usages of the library.
For example, it’s now possible to use a JsonDocument
as a global variable. Everybody knows I don’t recommend doing that, but many users do it already.
I’d be glad to hear your comments and suggestions about ArduinoJson 6.