Mar 22nd, 2025
Ceci est le deuxième article de la série
Flutter – Détection des fuites de mémoire
. Si vous avez manqué le premier et que vous souhaitez en savoir plus sur le fonctionnement interne du Garbage Collector de Dart, vous pouvez le faire
.
Dans cet article, je vais fournir des exemples de code sur la manière de libérer manuellement les objets que le Garbage Collector de Dart ne gérera pas automatiquement. Je vais essayer d'être aussi concis que possible, avec un exemple par section.
Les exemples couvrent tous les objets non collectables référencés dans le premier article, sauf le dernier, car la libération des bibliothèques tierces est liée à la documentation de la bibliothèque sélectionnée.
De nombreux objets Flutter ont une méthode
dispose
ou
cancel
. Assurez-vous d'abord de vérifier si votre objet potentiellement fuiteur en a une. L'AnimationController utilisé dans cet exemple en est un et est lié aux tickers du framework. Si ces ressources ne sont pas correctement libérées, elles peuvent entraîner des fuites de mémoire.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
)..repeat();
}
@override
void dispose() {
_controller.dispose(); // Dispose the AnimationController
super.dispose();
}
...
}
Les variables globales sont accessibles dans toute l'application et n'ont pas de fin de cycle de vie définie. C'est pourquoi vous devez les définir sur
null
lorsqu'elles ne sont plus nécessaires.
class AppState {
static var largeData = List<int>.filled(1000000, 0);
}
void cleanupGlobalVariables() {
AppState.largeData = null; // Free up memory if it is no longer needed
}
Le but principal d'un stream est de transmettre les données du stream quand et où elles sont nécessaires. Il n'est pas lié à une seule utilisation, et le Garbage Collector de Dart ne peut pas savoir quand les données du stream ne sont plus nécessaires. Annulez les abonnements aux streams lorsqu'ils ne sont plus nécessaires pour éviter les fuites de mémoire.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
StreamSubscription<int>? _subscription;
@override
void initState() {
super.initState();
_subscription = someStream.listen((data) {
print('Stream sent data: $data');
});
}
@override
void dispose() {
_subscription?.cancel(); // Cancel the subscription
super.dispose();
}
...
}
Le contexte de build de Flutter (
BuildContext
) est un objet de courte durée et de grande taille. N'utilisez jamais
BuildContext
à l'intérieur de fermetures si la fermeture SURVIT au widget. Si vous avez besoin du contexte dans une fermeture, assignez le contexte à une variable et utilisez ensuite cette variable à l'intérieur de la fermeture. Le Garbage Collector de Dart collecte automatiquement les variables.
// BAD
Widget build(BuildContext context) {
final printer = () => print(someFunctionUsingContext(context));
usePrinter(printer);
…
}
// GOOD
Widget build(BuildContext context) {
final result = someFunctionUsingContext(context); // variable gets GCed
final printer = () => print(result);
usePrinter(printer);
…
}
La situation avec les caches et les cartes est un peu similaire à celle des variables globales. Elles conservent les objets même lorsque vous n'en avez plus besoin en maintenant des références fortes vers eux. S'assurer que les caches et les cartes ne causent pas de fuites de mémoire indésirables inclut plusieurs approches, toutes viables et dépendantes de votre cas d'utilisation.
cache.remove('key'); // Remove an object
cache.clear(); // Clear the entire cache
final weakCache = WeakMap<Key, Value>();
weakCache[key] = BigObject(); // Doesn’t prevent garbage collection
final cache = LinkedHashMap<String, BigObject>();
void addToCache(String key, BigObject value) {
if (cache.length >= 100) {
cache.remove(cache.keys.first); // Remove the oldest object
}
cache[key] = value;
}
final imageCache = PaintingBinding.instance.imageCache;
imageCache.clear(); // Clear the image cache
Lorsqu'une minuterie est créée, les objets dans son callback forment une référence forte à elle. Lorsque l'objectif de la minuterie est dépassé, ces références fortes existent toujours et, sans libération manuelle, elles causeront des fuites de mémoire.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Timer _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
print("Tick-tock!");
});
}
@override
void dispose() {
_timer.cancel(); // Cancel the timer when the widget is disposed
super.dispose();
}
...
Les méthodes spécifiques à la plateforme doivent être gérées/libérées correctement. Un développeur doit gérer manuellement l'acquisition puis la libération de la ressource pour éviter les fuites de mémoire.
class WakeLockScreen extends StatefulWidget {
@override
_WakeLockScreenState createState() => _WakeLockScreenState();
}
class _WakeLockScreenState extends State<WakeLockScreen> {
static const platform = MethodChannel('com.example.wakelock');
@override
void initState() {
super.initState();
_acquireWakeLock();
}
Future<void> _acquireWakeLock() async {
try {
await platform.invokeMethod('acquireWakeLock');
} on PlatformException catch (e) {
print("Failed to acquire WakeLock: ${e.message}");
}
}
Future<void> _releaseWakeLock() async {
try {
await platform.invokeMethod('releaseWakeLock');
} on PlatformException catch (e) {
print("Failed to release WakeLock: ${e.message}");
}
}
@override
void dispose() {
_releaseWakeLock(); // Release the WakeLock when the widget is disposed
super.dispose();
}
...
Si vous pensez avoir libéré tous les objets non collectables, mais que votre application fuit encore, il est temps d'utiliser des outils qui vous aident à détecter où votre application fuit. Commençons par le plus simple,
.