I found a memory leak in the library!
The library is thoroughly tested; it’s is very unlikely that you found a memory leak. You’re probably using the library incorrectly.
What’s the problem?
The “leak” appears when you reuse a JsonDocument
without destroying or clearing it. Here is an example that shows this “leak”:
StaticJsonDocument<200> doc;
int counter;
void loop() {
doc["key"] = String("value") + counter++;
serializeJson(doc, Serial);
}
If you run this program, you’ll see something like this:
{"key":"value0"}
{"key":"value1"}
{"key":"value2"}
{"key":null}
{"key":null}
{"key":null}
{"key":null}
As you can see, all values after "value3"
are missing, showing that the JsonDocument
ran out of space.
Why does this happen?
JsonDocument
contains a monotonic allocator: a fast and lightweight allocator that cannot release memory. To release memory, you must either clear or destroy the JsonDocument
.
ArduinoJson uses this kind of allocator because it provides the best performance with the smallest possible code. Most users don’t notice the problem, but you can run into it if you reuse the same JsonDocument
without destroying or clearing it.
The program above runs out of memory because:
- It uses a
String
, forcing ArduinoJson to make a copy. - It uses a global
JsonDocument
. - It doesn’t clear the
JsonDocument
between each iteration.
ArduinoJson is a serialization library: it is designed to serialize and deserialize JSON documents.
Do not use a JsonDocument
not to store the state of your application.
How to fix this problem?
Solution 1: don’t use a global JsonDocument
The “leak” would not be a problem if the program were not reusing the same JsonDocument
.
Instead, it could just create a new one each time, like so:
int counter;
void loop() {
StaticJsonDocument<200> doc;
doc["key"] = String("value") + counter++;
serializeJson(doc, Serial);
}
If you use a StaticJsonDocument
, like the example above, there is virtually no impact on the performance because creating and destroying a StaticJsonDocument
is extremely fast.
Solution 2: clear the JsonDocument
We can eliminate the problem by releasing the memory at the beginning of each iteration.
To do that, simply call JsonDocument::clear()
, as in the snippet below:
StaticJsonDocument<200> doc;
int counter;
void loop() {
doc.clear();
doc["key"] = String("value") + counter++;
serializeJson(doc, Serial);
}
Solution 3: call garbageCollect()
If it’s not possible to clear the document, you can call JsonDocument::garbageCollect()
to reclaim the leaked memory.
StaticJsonDocument<200> doc;
int counter;
void loop() {
doc["key"] = String("value") + counter++;
doc.garbageCollect();
serializeJson(doc, Serial);
}
Internally, this function creates a copy of the document and replaces the original, so it’s a slow operation and temporarily requires a lot of memory.
See also
- JsonConfigFile.ino
- Why must I create a separate config object?
- How to reuse a
JsonDocument
? - The chapter “Inside Arduino” in Mastering ArduinoJson explains how the allocator is implemented.