Hamed Rahimvand
Hamed Rahimvand

Reputation: 630

How to find memory leaks in Flutter?

I need to find memory leaks in Flutter. How to find them? and how to create memory leaks for the test?

Upvotes: 33

Views: 39043

Answers (3)

Sathish Gadde
Sathish Gadde

Reputation: 1623

I am implemented Memory leak testing in android studio ide.

Step - 1 : Connect your device with android studio and run your application on your device.

Step - 2 : Go to View -> Tool Windows -> Flutter Performance

Step - 3 : Bottom of the window Open Dev Tools option will be there, click on it. It will be navigate into new window of your browser. See below image for more details :

enter image description here enter image description here

Step - 4 : To follow the steps below as per the screenshot, you can see the size and details of the object causing the memory leak. First Select Memory from available menus than you can able to see below ui. enter image description here

first: Click on settings icon

then: Mark down Dart and Flutter checkboxes.

and finally: Click on Apply button.

Step - 5 : This is final step, now you can able to see memory leaking info. enter image description here

first: Click on Snapshot it will be collect and display object list in bottom of the window.

and then: Click on search icon and Here you can see those classes which objects are not destroyed. Suppose am selected ApiRepository.dart class and instance will be available in memory ,so that details are visible in window. If multiple objects created than you can see here the total no. of instance and total size.

Step - 6 : You can able to call Garbage Collector manually by using GC icon . You can anytime Reset and get latest snapshot using Reset and Snapshot buttons.

For more information about Memory allocation related details read below articles :

  1. Offical document about memory leak on flutter.dev
  2. Article about garbage collector.

Upvotes: 37

IvanPavliuk
IvanPavliuk

Reputation: 1790

You can start from reading the official documentation - https://docs.flutter.dev/development/tools/devtools/memory

The next steps describe how to run the Memory view and how to create memory leaks manually:

  1. Press the "Open Flutter DevTools" button. It opens browser. In my case it's Safari (on Mac). If you see just a white screen, copy the link and paste it to Chrome.

enter image description here

  1. Select the "Memory" tab.

enter image description here

  1. Press on the graph. You will see a lot of values for the selected period of time. Take a look at "Dart/Flutter" memory usage. In my case it's 50.52 MB

enter image description here

  1. You can simulate significant amount of leaks using the next code:

     import 'package:flutter/cupertino.dart';
     import 'package:flutter/material.dart';
    
     class MemoryLeakObject {
       final String text;
    
       MemoryLeakObject(this.text);
     }
    
     List<MemoryLeakObject> leakObjects = [];
    
     class MemoryLeaksScreen extends StatelessWidget {
       @override
       Widget build(BuildContext context) {
         return Scaffold(
           body: Center(
             child: CupertinoButton(
               child: const Text(
                 'Create 1 000 000 leaks',
               ),
               onPressed: () {
                 while (leakObjects.length < 1000000) {
                   leakObjects.add(
                     MemoryLeakObject('Count: ${leakObjects.length}'),
                   );
                 }
               },
             ),
           ),
         );
       }
     }
    
  2. Open the screen with the code above and press the 'Create 1 000 000 leaks' button.

  3. Take a look at the graph again. In my case the "Dart/Flutter" memory usage increased to 101.28 MB. Also a Snapshot was created with all the objects in the memory. As you can see, there are 933365 objects of the "MemoryLeakObject" class.

enter image description here

Upvotes: 10

ch271828n
ch271828n

Reputation: 17567

If you want/need to add non-integration tests to reproduce/fix the memory leak, here is the approach. In short, it runs on your host by simply flutter test, and does not depend on a simulator/real-device at all.

Example usage: https://gist.github.com/fzyzcjy/e68c375643d7c77942cdc8fb5f01de18

Code (without example):

import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'dart:isolate';

import 'package:common_dart/utils/processes.dart';
import 'package:front_log/front_log.dart';
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart' hide Isolate, Log;
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:vm_service/vm_service_io.dart';

const _kTag = 'vm_services';

// #4657
FutureOr<void> runTestsInVmService(
  FutureOr<void> Function(VmServiceUtil) body, {
  required String selfFilePath,
}) async {
  Log.d(_kTag, 'runInVmService selfFilePath=$selfFilePath Platform.script.path=${Platform.script.path}');

  if (Platform.script.path == selfFilePath) {
    final vmService = await VmServiceUtil.create();
    tearDownAll(vmService.dispose);
    await body(vmService);
  } else {
    test(
      'run all tests in subprocess',
      // #4764
      timeout: const Timeout(Duration(seconds: 60)),
      () async {
        await executeProcess('dart', ['run', '--enable-vm-service', selfFilePath]);
      },
    );
  }
}

/// https://stackoverflow.com/questions/63730179/can-we-force-the-dart-garbage-collector
class VmServiceUtil {
  static const _kTag = 'VmServiceUtil';

  final VmService vmService;

  VmServiceUtil._(this.vmService);

  static Future<VmServiceUtil> create() async {
    final serverUri = (await Service.getInfo()).serverUri;
    if (serverUri == null) {
      throw Exception('Cannot find serverUri for VmService. '
          'Ensure you run like `dart run --enable-vm-service path/to/your/file.dart`');
    }

    final vmService = await vmServiceConnectUri(_toWebSocket(serverUri), log: _Log());
    return VmServiceUtil._(vmService);
  }

  void dispose() {
    vmService.dispose();
  }

  Future<void> gc() async {
    final isolateId = Service.getIsolateID(Isolate.current)!;
    final profile = await vmService.getAllocationProfile(isolateId, gc: true);
    Log.d(_kTag, 'gc triggered (heapUsage=${profile.memoryUsage?.heapUsage})');
  }
}

String _toWebSocket(Uri uri) {
  final pathSegments = [...uri.pathSegments.where((s) => s.isNotEmpty), 'ws'];
  return uri.replace(scheme: 'ws', pathSegments: pathSegments).toString();
}

class _Log extends vm_service.Log {
  @override
  void warning(String message) => Log.w(_kTag, message);

  @override
  void severe(String message) => Log.e(_kTag, message);
}

Upvotes: 0

Related Questions