作为Android系统应用最广泛的内存泄漏管理工具,LeakCanary已经迭代了多个版本。它的设计思想很值得学习,其中应用到很多Android底层源码方面的知识。
开始前的思考
- 如何监控Activity、Fragment生命周期终结? —— When
- 在监控到页面
onDestroy()
后,如何判断它的实例有没有被回收? —— Who - LeakCanary怎样实现自启动? —— How
背景知识
首先补充JVM的相关知识引用和GC,缺少这两部分知识是无法继续展开讨论的。
强软弱虚四大引用
强引用
描述必需对象,只有当JVM停止运行时才会销毁。
软引用
描述非必需对象。
内存足够的话,GC后就不会回收。
应用场景大多为缓存,如Bitmap、Activity。
val a = Object()
val softRef = SoftReference<Object>(a)
val a = softRef.get()
a = null
System.gc()
println("${softRef.get()}") // 非null
弱引用
GC时一定回收,不论内存是否充足。
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也会回收。
通常用来跟踪对象被垃圾回收的活动。
val queue = ReferenceQueue<String>()
val phantomRef = PhantomReference<String>("hello", queue)
println(phantomRef.get()) // null
引用队列
引用队列可以配合软、弱、虚引用使用,当引用的对象即将被JVM回收时,会将其加入引用队列中。
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
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
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callback);
}
}
在Activity.onDestroy()
方法最后,会将destroy时间通知监听者,获取到callbacks列表后,遍历触发onActivityDestroyed()
方法。
Activity.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
@Override
public void registerFragmentLifecycleCallbacks(@NonNull FragmentLifecycleCallbacks cb,
boolean recursive) {
mLifecycleCallbacks.add(new FragmentLifecycleCallbacksHolder(cb, recursive));
}
与Activity略有不同的是,Fragment生命周期发生变化时,不论进入哪一个阶段,都会走统一的setState()
函数,在setState()
中根据下一阶段不同,分发给不同的监听者,从而触发监听者的onFragmentDestroyed()
。
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
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,就会将它加入到引用队列中。
@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
// before 2.0
public void onCreate() {
super.onCreate()
LeakCanary.install(this);
}
After2.0,自动
2.0之后通过ContentProvider
实现自动注册,在《AMS源码分析》文中,介绍过创建Application过程中会自动加载ContentProvider。LeakCanary就是借助了这一点来实现的。
首先在Manifest文件中声明自身的provider。
Manifest.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时会自动调用。
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application) // <--重点:初始化
return true
}
InternalAppWatcher.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
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
Fragment注册监听
FragmentDestroyWatcher.kt
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的部分,不属于本文讨论范畴。