Detecting Memory Leaks in Flutter via Rendering Tree Analysis
By tracking the discrepancy between the number of EngineLayer objects actually rendered each frame and the total EngineLayer instances retained in native memory, developers can detect and pinpoint Flutter memory leaks caused by lingering Dart references, using SceneBuilder monitoring and WeakPersistentHandle inspection.
Background: Memory usage is a key performance metric for apps. Flutter lacks a convenient memory‑leak detection tool.
Flutter memory consists of virtual memory; developers mainly interact with Dart objects, while the Engine (C++) and Embedder (platform code) also consume memory. The total memory is the sum of DartVM memory and native memory.
Flutter architecture is divided into three layers: the Framework layer written in Dart, the Engine layer in C/C++ (Skia rendering), and the Embedder layer (e.g., Objective‑C/Swift on iOS, Java/Kotlin on Android).
The Dart binding layer connects Dart classes to C++ objects. For example, ui.SceneBuilder in Dart maps to flutter::SceneBuilder in C++. When a Dart constructor like ui.SceneBuilder() is called, the corresponding C++ constructor SceneBuilder_constructor creates a native instance, which is wrapped in a WeakPersistentHandle and injected into the Dart VM.
Because the native instance is reference‑counted (inherits from RefCountedDartWrappable), its reference count increases on creation and decreases when the Dart object becomes unreachable. When the count reaches zero, the C++ destructor runs, releasing the native memory.
WeakPersistentHandle objects are visible in Flutter’s Observatory tool; they hold pointers to native objects and are reclaimed only when the associated Dart object loses reachability.
Memory leaks often arise when Dart objects retain references (e.g., async callbacks holding a BuildContext) after a widget tree is removed, preventing the native objects from being garbage‑collected.
Detection strategy: compare the number of rendering‑tree nodes (EngineLayer objects) used in the current frame with the total number of EngineLayer instances stored in native memory. A persistent excess indicates a leak.
Implementation: monitor ui.SceneBuilder calls that add EngineLayer objects, record the count per frame, and plot the “used” vs. “in‑memory” layer curves. A divergence, such as an orange curve staying higher after a page is popped, signals a leak.
Visualization tools can also dump the layer tree structure and WeakPersistentHandle reference chains to pinpoint the offending widget or element.
Conclusion: Analyzing the rendering tree provides a generic way to locate memory leaks in Flutter and can be extended to other Dart objects. Developers should avoid long‑living async references to UI elements.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
