2分钟教你Flutter怎么避免引用内存泄漏

内存泄漏原因

所有语言的内存泄漏无非就是GC Root(线程,全局或静态数组或引用等)的引用链持有了需要释放的引用,导致无法释放。
解决释放的方法,无非就是2种,如下:
在这里插入图片描述

1. 在当前类,或者方法等移除改引用,让其他自动释放,等下一轮GC扫描释放。如



class B{
  void doSomething(){

  }
}

class A{
	B? b;
}
A a = A();
void test(){
  a.b = B();
  a.b?.doSomething();
}

void dispose(){
    a.b = null;
}


void main() {
  test();
  dispose();
}


A 这时候时全局的,如果使用完B后,想要B自动释放,只需要调用dispose() 将B置null就好。

然后,现实就是有可能,忘记调用dispose().导致B无法释放。怎么办呢?对,你猜到了,就是弱引用,Dart也有!

来了来了就是那个闪亮的仔, WeakReference(和Java的弱引用名字一样哦)

2. 使用弱引用-----WeakReference,当前使用完,生命周期结束后,自动释放。

在上面代码,B的生命周期,仅仅是在test这个方法里面,所以执行test后B就不需要用了,那么就让他自动等GC移除.
让我们看看他的源码

/// A weak reference to a Dart object.
///
/// A _weak_ reference to the [target] object which may be cleared
/// (set to reference `null` instead) at any time
/// when there is no other way for the program to access the target object.
///
/// _Being the target of a weak reference does not keep an object
/// from being garbage collected._
///
/// There are no guarantees that a weak reference will ever be cleared
/// even if all references to its target are weak references.
///
/// Not all objects are supported as targets for weak references.
/// The [WeakReference] constructor will reject any object that is not
/// supported as an [Expando] key.
///
/// Use-cases like caching can benefit from using weak references. Example:
///
/// ```dart
/// /// [CachedComputation] caches the computation result, weakly holding
/// /// on to the cache.
/// ///
/// /// If nothing else in the program is holding on the result, and the
/// /// garbage collector runs, the cache is purged, freeing the memory.
/// ///
/// /// Until the cache is purged, the computation will not run again on
/// /// a subsequent request.
/// ///
/// /// Example use:
/// /// ```
/// /// final cached = CachedComputation(
/// ///     () => jsonDecode(someJsonSource) as Object);
/// /// print(cached.result); // Executes computation.
/// /// print(cached.result); // Most likely uses cache.
/// /// ```
/// class CachedComputation<R extends Object> {
///   final R Function() computation;
///
///   WeakReference<R>? _cache;
///
///   CachedComputation(this.computation);
///
///   R get result {
///     final cachedResult = _cache?.target;
///     if (cachedResult != null) {
///       return cachedResult;
///     }
///
///     final result = computation();
///
///     // WeakReferences do not support nulls, bools, numbers, and strings.
///     if (result is! bool && result is! num && result is! String) {
///       _cache = WeakReference(result);
///     }
///
///     return result;
///   }
/// }
/// ```
("2.17")
abstract class WeakReference<T extends Object> {
 /// Creates a [WeakReference] pointing to the given [target].
 ///
 /// The [target] must be an object supported as an [Expando] key,
 /// which means [target] cannot be a number, a string, a boolean, a record,
 /// the `null` value, or certain other types of  special objects.
 external factory WeakReference(T target);

 /// The current object weakly referenced by [this], if any.
 ///
 /// The value is either the object supplied in the constructor,
 /// or `null` if the weak reference has been cleared.
 T? get target;
}

好家伙,上面就用例子叫我们怎么使用,属性target就是获取当前需要的引用。让我们改改上面


class B{
  void doSomething(){

  }
}

class A{
	WeakReference<B>? b;
}
A a = A();
void test(){
	a.b = WeakReference(B());
  a.b?.target?.doSomething();
}

void dispose(){
    a.b = null;
}


void main() {
  test();
  print(a.b?.target);
}

让我们执行下,看看结果
控制台:

Instance of ‘B’

??? ,有同学就问,怎么还持有,怎么还不释放。
因为当前没触发GC垃圾回收器,所以没必要进行回收。看过我这篇文章Android内存抖动
的同学知道,因为频繁回收会出现内存抖动现象,从而导致App掉帧甚至卡顿现象,GC垃圾回收器在这做了优化。那如果真的想测试验证这个结果,怎么办?
可以2种方案,第一种,老套路啦,强制制造大量无效垃圾引用,让GC触发回收。
如:制造大量的引用,并且需要释放的引用

void main() {
  test();
  List.generate(10000, (index) {
    ByteData byteData = ByteData(10000);
  });
  print(a.b?.target);
}


让我们执行下,看看结果
控制台:

null

当当当~,回收了吧

使用vm_service插件触发GC

还有一种办法,就是调用vm_service插件提供的VmService进行回收。注意一点是,这个GC做了优化,回收是单独对线程进行回收的,所以需要筛选对应的线程进行回收,如我们的线程基本是在UI线程也就是main线程中进行的,那么我们就筛选main


class VmServiceHelper {
  bool debug = false;
  VmServiceHelper() {
    assert(() {
      debug = true;
      return true;
    }());
  }
  VmService? _vmService;
  VM? _vm;
  Uri? _observatoryUri;




  Future<VmService?> getVmService() async {
    if (_vmService == null && debug) {
      ServiceProtocolInfo serviceProtocolInfo = await Service.getInfo();
      _observatoryUri = serviceProtocolInfo.serverUri;
      if (_observatoryUri != null) {
        Uri url = convertToWebSocketUrl(serviceProtocolUrl: _observatoryUri!);
        try {
          _vmService = await vmServiceConnectUri(url.toString());
        } catch (error, stack) {
          print(stack);
        }
      }
    }
    return _vmService;
  }

  Future try2GC() async {
    if (!debug) {
      return;
    }
    final vms = await getVmService();
    if (vms == null) return null;
    final isolate = await getMainIsolate();
    if (isolate?.id != null) {
      await vms.getAllocationProfile(isolate!.id!, gc: true);
    }
  }

  Future<Isolate?> getMainIsolate() async {
    if (!debug) {
      return null;
    }
    IsolateRef? ref;
    final vm = await getVM();
    if (vm == null) return null;
    var index = vm.isolates?.indexWhere((element) => element.name == 'main');
    if (index != -1) {
      ref = vm.isolates![index!];
    }
    final vms = await getVmService();
    if (ref?.id != null) {
      return vms?.getIsolate(ref!.id!);
    }
    return null;
  }

  Future<VM?> getVM() async {
    if (!debug) {
      return null;
    }
    return _vm ??= await (await getVmService())?.getVM();
  }
}

在使用之前,我们需要在运行配置上面加入

VsCode


 {
     ...
     "args": [
         "--disable-dds"
     ],
     "type": "dart"
 },


Android Studio
在这里插入图片描述

然后我们使用VmServiceHelper().try2GC(),代替上面的语句如下。但是需要运行app来测试。

test();
await VmServiceHelper().try2GC();
print(a.b?.target);

好了,如果本次文章觉得对你有用,请点赞,收藏,关注3连谢谢。
需要VmServiceHelper这个的朋友,可以到 flutter_leak_canary: ^1.0.1这个上面引用哦,如果顺手,给我点个Like哟

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值