作为Android系统应用最广泛的内存泄漏管理工具,LeakCanary已经迭代了多个版本。它的设计思想很值得学习,其中应用到很多Android底层源码方面的知识。
开始前的思考
- 如何监控Activity、Fragment生命周期终结? ------ When
- 在监控到页面
onDestroy()
后,如何判断它的实例有没有被回收? ------ Who - LeakCanary怎样实现自启动? ------ How
背景知识
首先补充JVM的相关知识引用和GC,缺少这两部分知识是无法继续展开讨论的。
强软弱虚四大引用
强引用
描述必需对象,只有当JVM停止运行时才会销毁。
软引用
描述非必需对象。
内存足够的话,GC后就不会回收。
应用场景大多为缓存,如Bitmap、Activity。
java
val a = Object()
val softRef = SoftReference<Object>(a)
val a = softRef.get()
a = null
System.gc()
println("${softRef.get()}") // 非null
弱引用
GC时一定回收,不论内存是否充足。
java
val s = "hello"
val weakRef = WeakReference<String>(s)
val ss = weakRef.get()
s = null
println("${weakRef.get()}") // 非null
System.gc() // System.gc() == Runtime.getRuntime().gc()
println("${weakRef.get()}") // null
虚引用
必须要结合引用队列ReferenceQueue
共同使用。
跟没有被引用一样,即使不GC也会回收。
通常用来跟踪对象被垃圾回收的活动。
java
val queue = ReferenceQueue<String>()
val phantomRef = PhantomReference<String>("hello", queue)
println(phantomRef.get()) // null
引用队列
引用队列可以配合软、弱、虚引用使用,当引用的对象即将被JVM回收时,会将其加入引用队列中。
java
private fun test() {
val queue = ReferenceQueue<>()
val s1 = "Hello"
val s2 = "World"
val weak1 = WeakReference(s1, queue)
val weak2 = WeakReference(s2, queue)
// 将s1、s2置空
s1 = null
s2 = null
var weakRef = WeakReference<String>()
while ((weakRef = queue.poll()) != null) { // 这一步不会输出任何内容
println("已回收:$weakRef")
}
System.gc()
while ((weakRef = queue.poll()) != null) { // 输出Hello World
println("已回收:$weakRef")
}
GC回收策略
Java虚拟机采用分代策略管理堆内存中的对象,分代的目的是优化GC性能 ,将具有不同生命周期 的对象归属于不同的年代,采取最适合它们的内存回收方式。
JVM运行时内存 可以分为堆(Heap
) 和非堆(Non-heap
) 两大部分。堆在JVM启动时创建,是运行时数据区域 ,所有类实例 和数组 内存从堆分配。堆以外的内存称为非堆内存,方法区、类结构 等数据保存在非堆内存。简单说,堆是开发者可以触及的内存部分,非堆是JVM自留的部分。
从对象分代的角度,JVM内部将对象分为3类:
New Generation
,新生代,位于堆内存,内部分为3块,其中Eden
和Survivor
的默认比例为8:1Eden
,新创建的对象都位于该分区,满时执行GC,并将仍存活的对象复制到From
,GC后此区域被清空From Survivor
,GC时,将仍存活的对象复制到To
To Survivor
,满时,将仍存活的对象复制到Old
。GC之后会交换From
和To
区,从而使新的To
(也就是老的From
)永远是空的
Old Generation
,老年代,位于堆内存Permanent Generation
,永生代,位于非堆内存
GC分类
Minor GC
,当Eden
区满时触发,触发频率较高,回收New Generation
。垃圾回收采用复制 算法,特点是简单高效 ,适用于存活对象较少 的情况,由于年轻代的对象生命周期较短 ,适用于复制算法进行快速垃圾回收。由于复制算法需要额外的空间 进行赋值操作,故处理大对象时会有一些额外开销 。Full GC
,回收Permanent
,New
&Old
,对整个Heap区进行回收。不同的JVM实现Full GC时,可能采取不同的算法,常见的有标记-清除、标记-整理、分代收集 。如下原因会导致Full GC:Old
被写满Permanent
被写满- 显式调用
System.gc()
Runtime.getRuntime().gc()
(两者等价)
GC Roots
定义:通过一系列名为GCRoots
的对象作为起始点,从这个节点向下搜索,搜索走过的路径称为ReferenceChain,当一个对象到GCRoots
没有任何ReferenceChain
相连时,(图论:这个对象不可到达),则证明这个对象不可用。
共有4类GC Roots
:
JavaStack
中的引用的对象。- 方法区中静态引用指向的对象。
- 方法区中常量引用指向的对象。
Native
方法中JNI
引用的对象。
Q1:监控生命周期
首先明确内存泄漏的概念,当对象不再使用后,理想的状况是把它占用的内存释放掉,以便其它对象新建时有充足的内存可以申请。如果连续内存不足以分配给新建的对象,就会导致OutOfMemory
异常。
在Android中主要指Activity,因为Activity持有的对其他对象的引用众多,一旦Activity发生泄漏,造成的负面影响是巨大的。而且由于用户可以反复退出重进某一页面,会使泄漏的Activity不断增多。
因此,我们需要在Activity和Fragment发生onDestroy()
后及时将其释放,避免泄漏。
监控Activity
先上结论,在Application.java
中提供了ActivityLifecycleCallbacks
的接口,通过Application.registerActivityLifecycleCallbacks()
可以注册Activity生命周期的监听。
Application.java
java
public interface ActivityLifecycleCallbacks {
// onCreate
default void onActivityPreCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
}
void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);
default void onActivityPostCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
}
// onStart
default void onActivityPreStarted(@NonNull Activity activity) {
}
void onActivityStarted(@NonNull Activity activity);
default void onActivityPostStarted(@NonNull Activity activity) {
}
// onResume
default void onActivityPreResumed(@NonNull Activity activity) {
}
void onActivityResumed(@NonNull Activity activity);
default void onActivityPostResumed(@NonNull Activity activity) {
}
// onPause
default void onActivityPrePaused(@NonNull Activity activity) {
}
void onActivityPaused(@NonNull Activity activity);
default void onActivityPostPaused(@NonNull Activity activity) {
}
// onStop
default void onActivityPreStopped(@NonNull Activity activity) {
}
void onActivityStopped(@NonNull Activity activity);
default void onActivityPostStopped(@NonNull Activity activity) {
}
// onSaveInstance
default void onActivityPreSaveInstanceState(@NonNull Activity activity,
@NonNull Bundle outState) {
}
void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState);
default void onActivityPostSaveInstanceState(@NonNull Activity activity,
@NonNull Bundle outState) {
}
// onDestroy
default void onActivityPreDestroyed(@NonNull Activity activity) {
}
void onActivityDestroyed(@NonNull Activity activity);
default void onActivityPostDestroyed(@NonNull Activity activity) {
}
}
可以看到,对于每个生命周期阶段,接口都提供了 pre-on-post
三个回调。解题思路就有了:
- 在
onActtivityCreated()
时,将Activity注册为弱引用,并关联到引用队列 - 在
onActivityDestroyed()
中触发GC,然后判断弱引用对象的get()
是否为null,如果非null说明发生泄漏,使用引用队列帮助我们判断,如果对象发生回收,引用队列poll()
会返回非空结果
registerActivityLifecycleCallbacks
注册时,将传入的callback放在名为mActivityLifecycleCallbacks
的列表中,列表的类型是ArrayList,在插入时需要对对象上锁,防止并发问题。
Application.java
java
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callback);
}
}
在Activity.onDestroy()
方法最后,会将destroy时间通知监听者,获取到callbacks列表后,遍历触发onActivityDestroyed()
方法。
Activity.java
java
protected void onDestroy() {
...
dispatchActivityDestroyed(); // <--重点:通知监听
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
}
private void dispatchActivityDestroyed() {
Object[] callbacks = collectActivityLifecycleCallbacks(); // <--重点:获取Application中的callbacks
if (callbacks != null) {
for (int i = callbacks.length - 1; i >= 0; i--) {
((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(this);
}
}
getApplication().dispatchActivityDestroyed(this);
}
监控Fragment
在Activity.onCreate()
中注册监听FramgentManager.registerFragmentLifecycleCallbacks()
,函数实现位于FragmentManagerImpl
类,同样是把callbacks
加入到mLifecycleCallbacks
列表。
FragmentManagerImpl.java
java
@Override
public void registerFragmentLifecycleCallbacks(@NonNull FragmentLifecycleCallbacks cb,
boolean recursive) {
mLifecycleCallbacks.add(new FragmentLifecycleCallbacksHolder(cb, recursive));
}
与Activity略有不同的是,Fragment生命周期发生变化时,不论进入哪一个阶段,都会走统一的setState()
函数,在setState()
中根据下一阶段不同,分发给不同的监听者,从而触发监听者的onFragmentDestroyed()
。
java
void dispatchOnFragmentDestroyed(@NonNull Fragment f, boolean onlyRecursive) {
if (mParent != null) {
FragmentManager parentManager = mParent.getFragmentManager();
if (parentManager instanceof FragmentManagerImpl) {
((FragmentManagerImpl) parentManager)
.dispatchOnFragmentDestroyed(f, true);
}
}
for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
if (!onlyRecursive || holder.mRecursive) {
holder.mCallback.onFragmentDestroyed(this, f);
}
}
}
小结
以上就是Activity和Fragment的生命周期监听,通过设置相应的监听回调来实现。
Q2:在监控到页面onDestroy()
后,如何判断它的实例有没有被回收? ------ Who
用一个弱引用去引用Activity实例,然后调用GC,如果get()
返回null,说明它被回收了,没有泄漏。之所以使用弱引用,是为了让它与Activity在GC时同步被回收。
更进一步,引用队列可以更好地满足我们的需求,如果Activity/Fragment对象处于可回收的状态,会自动进入引用队列。
以Activity的监听过程为例,当发生onDestroy()
时,会触发objectWatcher.watch()方
法。而在ObjectWatcher
类中,就是通过引用队列来判断Activity对象是否进行回收的。
ActivityDestroyWatcher.kt
java
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
if (configProvider().watchActivities) {
objectWatcher.watch(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
}
开始监听时会把Activity放入到watchList
里,在onDestroy()
时如果对象已经/可以被回收(位于引用队列),将其从watchList
移除。这样剩下来的对象就是发生了泄漏。
当弱引用对象的状态已经处于"可回收"时,无需经过gc,就会将它加入到引用队列中。
java
@Synchronized fun watch(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
removeWeaklyReachableObjects() // <--重点:找出已经加入引用队列的对象(可以被回收),然后将其从watchList中移除,这样watchList中剩下的就是还没有被回收的对象
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
SharkLog.d {
"Watching " +
(if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
(if (description.isNotEmpty()) " ($description)" else "") +
" with key $key"
}
watchedObjects[key] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
private fun removeWeaklyReachableObjects() {
// 重点:注意下面一段注释,翻译过来是:只要对象可以回收,无须经过gc,它就会被放进在引用队列中
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
Q3:自动化启动
2.0之前需要手动启动,2.0之后自动。
Before2.0,手动
Application.java
java
// before 2.0
public void onCreate() {
super.onCreate()
LeakCanary.install(this);
}
After2.0,自动
2.0之后通过ContentProvider
实现自动注册,在《AMS源码分析》文中,介绍过创建Application过程中会自动加载ContentProvider。LeakCanary就是借助了这一点来实现的。
首先在Manifest文件中声明自身的provider。
Manifest.xml
xml
<application>
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false" />
</application>
AppWatcherInstaller
负责创建Activity、Fragment监听,它的onCreate()
函数在集成APP时会自动调用。
java
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application) // <--重点:初始化
return true
}
InternalAppWatcher.java
java
fun install(application: Application) {
checkMainThread()
if (this::application.isInitialized) {
return
}
InternalAppWatcher.application = application
if (isDebuggableBuild) {
SharkLog.logger = DefaultCanaryLog()
}
val configProvider = { AppWatcher.config }
ActivityDestroyWatcher.install(application, objectWatcher, configProvider) // <--注册Activity监听
FragmentDestroyWatcher.install(application, objectWatcher, configProvider) // <--注册Fragment监听
onAppWatcherInstalled(application)
}
Activity注册监听
伴生对象(静态函数)中,对Application对象调用registerActivityLifecycleCallbacks()
函数,完成Activity监听注册。
ActivityDestroyWatcher.kt
java
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
Fragment注册监听
FragmentDestroyWatcher.kt
java
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> AppWatcher.Config
) {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
if (SDK_INT >= O) { // <--不低于26
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
)
}
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}
if (fragmentDestroyWatchers.size == 0) {
return
}
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) { // <--当Activity.onCreate()时,注册Fragment监听
for (watcher in fragmentDestroyWatchers) {
watcher(activity)
}
}
})
}
以上就是Activity、Fragment生命周期监听的自动化注册过程,Fragment要比Activity多一步,无法直接通过Application进行注册。
总结
LeakCanary工作流程图可以总结如下,另外还有Dump & Analyze Heap的部分,不属于本文讨论范畴。