一、前言
Flutter 所使用的 Dart 语言具有垃圾回收机制,有垃圾回收就避免不了会内存泄漏。在 Android 平台上有个内存泄漏检测工具 LeakCanary[1], 它可以方便地在 debug 环境下检测当前页面是否泄漏。本文将会带你实现一个 Flutter 可用的 LeakCanary,并讲述怎么用该工具检测出了 1.9.1 Framework 上的两个泄漏。
二、Dart 中的弱引用
在具有垃圾回收的语言中,弱引用是检测对象是否泄漏的一个好方式。我们只需弱引用观测对象,等待下次 Full GC, 如果 GC 之后对象为 null
,说明被回收了, 如果不为 null
就可能是泄漏了。
Dart 语言中也有着弱引用,它叫 Expando<T>
, 看下它的 API:
class Expando<T> {
external T operator [](Object object "");
external void operator []=(Object object, T value);
}
你可能会好奇上述代码弱引用体现在哪里呢?其实是在 expando[key]=value
这个赋值语句上。Expando
会以弱引用的方式持有 key
,这里就是弱引用的地方。
那么问题来了,这个 Expando
弱引用持有的是 key
, 但是本身又没有提供 getKey()
这样的 API, 我们就无从下手去得知 key
这个对象是否被回收了。
为了解决这个问题,我们来看下 Expando
的具体实现, 具体的代码在 expando_path.dart[2]:
@path
class Expando<T> {
// ...
T operator [](Objet object "") {
var mask = _size - 1;
var idx = object._identityHashCode & mask;
// sdk 是把 key 放到了一个 _data 数组内,这个 wp 是个 _WeakProperty
var wp = _data[idx];
// ... 省略部分代码
return wp.value;
// ... 省略部分代码
}
}
注意: 此 patch 代码不适用于 Web 平台
我们可以发现这个 key
对象是放到了 _data
数组内, 用了一个 _WeakProperty
来包裹,那么这个 _WeakProperty
就是关键类了, 看下它实现,代码在 weak_property.dart[3]:
@pragma("vm:entry-point")
class _WeakProperty {
get key => _getKey();
// ... 省略部分代码
_getKey() native "WeakProperty_getKey";
// ... 省略部分代码
}
这个类有我们想要的 key
,可以用于判断对象是否还在。
怎么获取这种私有属性和变量呢?Flutter 中的 Dart 是不支持反射的(为了优化打包体积,关闭了反射), 有没有其他办法来获取到这种私有属性呢?
答案肯定是 “有”,为了解决上述问题, 我来向大家介绍一个 Dart 自带的服务——Dart VM Service。
三、Dart vm_service
Dart VM Service[4] (后面简称 vm_service
) 是 Dart 虚拟机内部提供的一套 Web 服务,数据传输协议是 JSON-RPC 2.0。不过我们并不需要要自己去实现数据请求解析, 官方已经写好了一个可用的 Dart SDK 给我们用:`vm_service`[5]。
ObjRef
, Obj
和 id
的作用
先介绍 vm_service
中的核心内容:ObjRef
、Obj
、id
vm_service
返回的数据主要分为两大类,ObjRef
(引用类型) 和 Obj
(对象实例类型)。其中 Obj
完整的包含了 ObjRef
的数据, 并在其基础上增加了额外信息 (ObjRef
只包含了一些基本信息,例如