Memory Leak In Dart/Flutter

mobile academy

In the previous article we learned about Dart Memory Management and now let us learn about the Memory Leak In Dart/Flutter.

Flutter uses three types of languages

  1. The framework layer is written in Dart for application layer development.
  2. The engine layer is written in C/C++ and used for graphics rendering.
  3. The embedder layer is written in the embedding layer languages. For example, iOS uses Objective-C/swift, while Android uses Java/Kotlin.

When it comes to the memory used by Flutter, it refers to the sum memory of the three layers and it’s a virtual memory that is assigned to app, it does not have to do anything with Physical memory in your device.

dart/flutter memory

What is a Memory Leak?

In computer science, a memory leak is a type of resource leak that occurs when a computer program incorrectly manages memory allocations in a way that memory which is no longer needed is not released.

Source Wikipedia

Memory leaks happen in Dart?

As we saw previously in Dart Memory Management are done automatically using a Garbage collector but it cannot prevent all types of Memory Leak In Dart/Flutter, and developers still need to watch objects that can lead to memory leak.

How do memory leaks occur?

Memory Leak In Dart/Flutter can happen when an object is still being referenced in (heap) memory but is no longer being used. Some common cause is the presence of unused objects that remain in memory due to caching, improper use on BuildContext or failure to remove listeners when they are no longer needed.

Why can’t the garbage collector prevent all leaks?

While the garbage collector takes care of all unreachable objects, it’s the responsibility of the application to ensure that unneeded objects are no longer reachable (referenced from the root object).

So, if non needed objects are left referenced (in a global or static variable, or as a field of a long-living object), the garbage collector can’t recognize them, the memory allocation grows progressively, and the app eventually crashes with an out-of-memory error.

here is an example

In the above example we have a Timer callback but we have no way to cancel the timer once the task is completed, Timer will hang around in memory unless we cancel it, which can lead to Memory Leak.

In the above example we are passing leak object to LeakScreen(leak: leak) since we are constructing a Leak object in RowWidget class, as long as the root object has a reference to RowWidget Leak object can’t be GC which can lead to Memory Leak, we need to make sure that RowWidget is properly disposed once it’s not required.

How to Fix a above code?

Here is the example

In the above class we have added a dispose() to clean all the unwanted resource allocation

In the above example we have move the Leak class from constructor to state class and initialized it in initState method and properly dispose it in dispose method, in this way Leak object is ready for GC as soon as we dispose the LeakScreen

Here is an another example

In the example above, _MyWidgetState subscribes to someStream in its initState method. However, it does not unsubscribe from the stream when the widget is disposed. This could cause a memory leak if someStream continues to produce data even after MyWidget has been removed from the widget tree.

How to fix the above code?

In the above example we have added a dispose method in which we have cancel the Stream subscription.

How to prevent a Memory Leak in Flutter?

Here are some of the point that you can follow to prevent a Memory leak in Flutter:

  • Proper Disposal of Objects: Always dispose of objects when they are no longer needed by utilizing the disposal method in the Stateful Widget. This ensures that any associated resources are released and prevents unnecessary memory consumption.
  • Manage Streams Properly: When subscribing to a Stream, make sure to unsubscribe (cancel the subscription) when you’re done using it. This is particularly important in the case of StatefulWidgets, where you commonly subscribe in initState() and should unsubscribe in dispose().
  • Avoid Long-Lived Global Keys: If you use a GlobalKey and it’s referenced globally, this could lead to memory leaks. Instead, consider using other types of keys like LocalKey or ValueKey, or pass the context as a parameter.
  • Manage Controllers: If you’ve created any controllers (like TextEditingController, AnimationController, etc.), they should be disposed of when the widgets using them are disposed to prevent memory leaks.
  • Avoid Persisting BuildContext: Don’t persist the BuildContext in a long-lifetime object or class. The context should exist during the widget’s lifecycle and should be discarded when the widget is disposed.
  • Resolve Callbacks: If you’ve attached callbacks to a widget like Timer.periodic, and that widget gets disposed, the callback may still hang around in memory. If the callback references other objects, those will also be retained. Be sure to nullify your callbacks where appropriate.

Memory leak vs memory bloat

In a Memory leak, an application progressively uses memory, for example, by repeatedly creating a listener, but not disposing it.

Memory bloat uses more memory than is necessary for optimal performance, for example, by using overly large images or keeping streams open through their lifetime.

Both leaks and bloats, when large, cause an application to crash with an out-of-memory error. However, leaks are more likely to cause memory issues, because even a small leak, if repeated many times, leads to a crash.

Become a Job Ready Flutter developer
Select your currency
INR Indian rupee