精华内容
下载资源
问答
  • 源码分析基于:1.6.3 对于Android开发者而言,内存泄漏是一种很常见的问题。LeakCanary就是捕获内存泄漏的一把利器。我们在这里就分析一下它的工作原理。   一、使用方法 使用方法就是我们在Application中添加代码...
  • LeakCanary源码解析

    2020-02-26 18:02:47
    3.2 RefWatcher.watch() 3.2.1 RefWatcher.watch()源码分析 3.2.2 RefWatcher.watch()小结 3.3 HeapDumper.dumpHeap() 3.4 HeapDumpListener.analyze() 3.5 LeakCanary源码分析总结 4 LeakCanary 2.x(2.2) 1 Java...

    1 Java和C/C++语言的内存泄漏

    在C/C++语言开发过程中,比如C语言 malloc 分配内存 free 释放内存,C++是 new Object 分配内存 delete object 释放内存,对象的内存分配回收都需要程序员下意识的去维护,否则很容易出现内存泄漏。

    但在Java中却没有这种情况,我们使用Java开发很多时候只需要 new Object 创建对象,使用对象后的无效内存都让GC回收了。但也存在GC无法回收的情况导致JVM内存泄漏:长周期的对象引用一直持有着短周期的对象引用,GC以为短周期对象引用还被使用,GC没有回收,所以导致了内存泄漏。

    那么,Java是怎么判断对象的引用还被使用?Java垃圾回收机制是怎样的?

    2 垃圾回收机制

    2.1 引用计数法

    引用计数法 简单理解就是记录一个对象被引用的次数,一个引用被使用引用计数器就+1,反之就-1,当引用次数为0就说明是一个垃圾对象可以被回收了。

    引用计数法实现非常简单,但也有存在的问题:循环引用,即对象a和对象b各自持有双方的引用,导致GC无法回收,也就导致内存泄漏。

    在这里插入图片描述

    2.2 可达性分析法

    可达性分析法 根据是否被GC Root引用确认是否是垃圾对象要被GC回收。

    可以作为GC Root的对象有:

    • 在线程栈中的局部变量(即正在被调用的方法里面的参数和局部变量)

    • 存活的线程对象

    • JNI的引用

    • Class对象(在Android中Class被加载后是不会被卸载的)

    • 引用类型的静态变量

    在这里插入图片描述

    3 LeakCanary 1.6.x(1.6.3)

    3.1 LeakCanary.install()

    3.1.1 LeakCanary.install()源码分析

    LeakCanary 是内存泄漏检测工具,在使用上一句代码即可(使用LeakCanary版本为1.6.3):

    public class MyApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            // LeakCanary是在另外一个进程中启动
            if (LeakCanary.isInAnalyzerProcess(this)) {
                return;
            }
            LeakCanary.install(this);
        }
    }
    

    通过一句代码 LeakCanary.install(this) 就可以实现监听内存泄漏。具体看下原理:

    LeakCanary.java
    
    public final class LeanCanary {
    	public static @NonNull RefWatcher install(@NonNull Application application) {
    		return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
    			.excludeRefs(AndroidExcludedRefs.createAppDefaults().build()
    			.buildAndInstall();
    	}
    
    	public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
    		return new AndroidRefWatcherBuilder(context);
    	}
    }
    
    AndroidRefWatcherBuilder.java
    
    public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
    	public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
    		@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    		enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
    		return headDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
    	}
    }
    

    上面的代码有几点需要说明:

    • DisplayService 是发生内存泄漏时的通知服务

    • excludedRefs() 是排除Android源码出现的内存泄漏问题

    最主要的是 AndroidRefWatcherBuilder.buildAndInstall()

    AndroidRefWatcherBuilder.java
    
    public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
    	private boolean watchActivities = true;
    	private boolean watchFragments = true;
    
    	public @NonNull RefWatcher buildAndInstall() {
    		if (LeakCanaryInternals.installedRefWatcher != null) {
          		throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
        	}
        	RefWatcher refWatcher = build();
        	if (refWatcher != DISABLED) {
        		// 根据app包名生成LeakCanary关联应用
        		if (enableDisplayLeakActivity) {
    				LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
    			}
    			// 监听Activity
    			if (watchActivities) {
    				ActivityRefWatcher.install(context, refWatcher);
    			}
    			// 监听Fragment
    			if (watchFragments) {
    				FragmentRefWatcher.Helper.install(context, refWatcher);
    			}
    		}
    		LeakCanaryInternals.installRefWatcher = refWatcher;
    		return refWatcher;
    	}
    } 
    
    LeakCanaryInternals.java
    
    public final class LeakCanaryInternals {
    	public static void setEnabledAsync(Context context, final Class<?> componentClass, final boolean enabled) {
    		final Context appContext = context.getApplicationContext();
    		// 开启子线程处理
    		AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnnable() {
    			@Override public void run() {
    				setEnabledBlocking(appContext, componentClass, enabled);
    			}
    		});
    	}
    
    	public static void setEnabledBlocking(Context appContext, Class<?> componentClass, boolean enabled) {
    		ComponentName component = new ComponentName(appContext, componentClass);
    		PackageManager packageManager = appContext.getPackageManager();
    		int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
    		// Blocks on IPC.
    		packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
    	}
    }
    
    ActivityRefWatcher.java
    
    public final class ActivityRefWatcher {
    	public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
    		Application application = (Application) context.getApplicationContext();
    		ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
    
    		// 最终是通过在Application注册监听每个Activity的生命周期,然后转发给RefWatcher
    		application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
    	}
    
    	private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = 
    		new ActivityLifecycleCallbacksAdapter() {
    			@Override public void onActivityDestroyed(Activity activity) {
    				refWatcher.watch(activity);
    			}
    		}
    }
    
    FragmentRefWatcher.java
    
    public interface FragmentRefWatcher {
    
    	final class Helper {
    		public static void install(Context context, RefWatcher refWatcher) {
    			List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
    
    			if (SDK_INT >= O) {
    				fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
    			}
    
    			try {
    				Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
    				Constructor<?> constructor = fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
    				FragmentRefWatcher supportFragmentRefWatcher = 
    					(FragmentRefWatcher) constructor.newInstance(refWatcher);
    				fragmentRefWatchers.add(supportFragmentRefWatcher);
    			} catch (Exception ignored) {
    			}
    
    			if (fragmentRefWatchers.size() == 0) {
    				return;
    			}
    
    			Helper helper = new Helper(fragmentRefWatcher);
    
    			// 和Activity一样也是监听Fragment生命周期转发给RefWatcher
    			Application application = (Application) context.getApplication();
    			application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
    		}
    
    		private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = 
    			new ActivityLifecycleCallbacksAdapter() {
    			@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    				for (FragmentRefWatcher watcher : fragmentRefWatchers) {
    					watcher.watchFragments(activity);
    				}
    			}
    		}
    	}
    }
    
    AndroidOFragmentRefWatcher.java
    
    class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
    	private final RefWatcher refWatcher;
    
    	AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
    		this.refWatcher = refWatcher;
    	}
    
    	private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = 
    		new FragmentManager.FragmentLifecycleCallbacks() {
    			@Override publci void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
    				View view = fragment.getView();
    				if (view != null) {
    					refWatcher.watch(view);
    				}
    			}
    
    			@Override
    			public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
    				refWatcher.watch(fragment);
    			}
    		}
    
    	@Override public void watchFragments(Activity activity) {
    		FragmentManager fragmentManager = activity.getFragmentManager();
    		fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
    	}
    }
    
    

    上面的处理可以用两个步骤说明:

    • 根据app的包名生成 LeakCanary 的关联应用

    • 通过 Application.registerActivityLifecycleCallbacks() 监听Activity以及 FragmentManager.registerFragmentLifecycleCallbacks() 监听Fragment的生命周期并转发给对应 RefWatcher 处理

    这一步生命周期监听处理简单理解就是:

    final RefWatcher refWatcher = xxxx;
    
    // 监听Activity生命周期
    application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() {
    	@Override
    	public void onActivityDestroyed(Activity activity) {
    		watcher.watch(activity);
    	}
    });
    
    // 监听Fragment生命周期
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            FragmentManager fm = activity.getFragmentManager();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                fm.registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
                    @Override
                    public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
                        View view = f.getView();
                        if (view != null) {
                            watcher.watch(view);
                        }
                    }
    
                    @Override
                    public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
                        watcher.watch(f);
                    }
                }, true);
            }
        }
    });
    

    3.1.2 LeakCanary.install()小结

    总结一下我们调用 LeakCanary.install() 的处理:

    • 创建 AndroidRefWatcherBuilder 构建 LeakCanary 所需参数,如提供 DisplayLeakService 内存泄漏通知服务,排除Android系统源码出现的内存泄漏

    • 创建 RefWatcher,关联app包名生成 LeakCanary 应用,通过 registerXxxLifecycleCallbacks() 监听生命周期转发给 RefWatcher 处理

    3.2 RefWatcher.watch()

    3.2.1 RefWatcher.watch()源码分析

    RefWatcher.java
    
    public final class RefWatcher {
    	public void watch(Object watchedReference) {
    		watch(watchReference, "");
    	}
    	
    	public void watch(Object watchReference, String referenceName) {
    		if (this == DISABLED) {
    			return;
    		}
    		final long watchStartNanoTime = System.nanoTime();
    		// 为监听引用watchReference(Activity或Fragment)生成唯一ID
    		String key = UUID.randomUUID().toString();
    		retainedKeys.add(key);
    		// watchReference就是监听的引用对象
    		final KeyedWeakReference reference = 
    			new KeyedWeakReference(watchReference, key, referenceName, queue);
    		ensureGoneAsync(watchStartNanoTime, reference);
    	}
    
    	private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    		watchExecutor.execute(new Retryable() {
    			@Override public Retryable.Result run() {
    				return ensureGone(reference, watchStartNanoTime);
    			}
    		});
    	}
    }
    

    上面的代码有一个对象需要注意:KeyedWeakReference

    LeakCanary 是通过 KeyedWeakReference 来确认 watchReference(Activity or Fragment) 是否已经被回收的。我们可以通过弱引用的 ReferenceQueue 确认队列中是否有数据,如果有就说明 watchReference 被GC回收了。具体可以看我写的一篇文章:Java的四种引用类型

    还有 watchExecutor 又是什么?这需要回到 RefWatcher 被构建时的代码:

    RefWatcherBuilder.java
    
    public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
    	public final RefWatcher build() {
    		...
    
    		WatchExecutor watchExecutor = this.watchExecutor;
    		if (watchExecutor == null) {
    			// AndroidWatchExecutor
    			watchExecutor = defaultWatchExecutor();
    		}
    
    		...
    
    		return new RefWatcher(watchExecutor, ...);
    	}
    }
    
    AndroidRefWatcherBuilder.java
    
    public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
    	return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
    }
    
    AndroidWatchExecutor.java
    
    public final class AndroidWatchExecutor implements WatchExecutor {
    	private final Handler mainHandler;
    	private final hnadler backgroundHandler;
    	private final long initialDelayMillis;
    	private final long maxBackoffFactor;
    	
    	public AndroidWatchExecutor(long initialDelayMillis) {
    		mainHandler = new Handler(Looper.getMainLooper());
    		HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
    		handlerThread.start();
    		backgroundHandler = new Handler(handlerThread.getLooper());
    		this.initialDelayMillis = initialDelayMillis;
    		maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
    	}
    
    	@Override public void execute(@NonNull Retryable retryable) {
    		// 第一次进入肯定是主线程,因为RefWatcher是在主线程创建
    		if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
    			waitForIdle(retryable, 0);
    		} else {
    			postWaitForIdle(retryable, 0);
    		}
    	}
    
    	private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
    		mainHandler.post(new Runnable() {
    			@Override public void run() {
    				waitForIdle(retryable, failedAttempts);
    			}
    		});
    	}
    
    	private void waitForIdle(final Retryable retryable, final int failedAttempts) {
    		// This needs to be called from the main thread
    		// 保证在主线程处于空闲状态时处理
    		// 因为只有主线程生命周期都走完后才有可能正常垃圾回收
    		Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    			@Override public boolean queueIdle() {
    				postToBackgroundWithDelay(retryable, failedAttempts);
    				return false; // 返回false表示只会执行一次,否则会一直执行
    			}
    		});
    	}
    
    	private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
    		long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
    		long delayMillis = initialDelayMillis * exponentialBackoffFactor;
    		backgroundHandler.postDelay(new Runnable() {
    			@Override public void run() {
    				// 在子线程执行Retryable
    				Retryable.Result result = retryable.run();
    				if (result == RETRY) {
    					postWaitForIdle(retryable, failedAttempts + 1);
    				}
    			}
    		}, delayMillis);
    	}
    }
    

    watchExecutorAndroidWatchExecutor,在 RefWatcher 创建的时候会在子线程执行 Retryable.run()

    接下来继续看 Retryable.run() 往后的操作:

    public final class RefWatcher {
    	Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    		long gcStartNanoTime = System.nanoTime();
    		long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    
    		// 将GC掉的对象从内存泄漏的怀疑名单中移除
    		removeWeaklyReachableReference();
    	
    		if (debuggerControl.isDebuggerAttached()) {
    			// The debugger can create false leaks.
    			return RETRY;
    		}
    		// 如果名单没有内存泄漏的引用对象
    		// 说明在某次GC已经回收对象,没有内存泄漏,不需要处理
    		if (gone(reference)) {
    			return DONE;
    		}
    		// 执行一次GC
    		gcTrigger.runGc();
    
    		// 再检查引用对象是否被回收
    		removeWeaklyReachableReferences();
    		// 引用对象没有被回收还在怀疑名单,说明已经内存泄漏,dump
    		if (!gone(reference)) {
    			long startDumpHeap = System.nanoTime();
    			long gcDurationMs = NANOSECONDS.toMills(startDumpHeap - gcStartNanoTime);
    
    			// 创建dump文件,创建通知提示dump
    			File heapDumpFile = heapDumper.dumpHeap();
    			// dump文件创建失败,重试
    			if (heapDumpFile == RETRY_LATER) {
    				// Could not dump the heap.
    				return RETRY;
    			}
    			long heapDumpDurationMs = NANOSECONDS.toMills(System.nanoTime() - startDumpHeap);
    
    			HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referencekey(reference.key)
    				.referenceName(reference.name)
    				.watchDurationMs(watchDurationMs)
    				.gcDurationMs(gcDurationMs)
    				.heapDumpDurationMs(heapDumpDurationMs)
    				.build();
    
    			// 分析dump文件
    			heapdumpListener.analyze(heapDump);
    		}
    		return DONE;
    	}
    
    	private boolean gone(KeyedWeakReference reference) {
    		return !retainedKeys.contains(reference.key);
    	}
    
    	private void removeWeaklyReachableReference() {
    		// WeakReferences are enqueued as soon as the object to which they point to be becomes weakly
    		// reachable. This is before finalization or garbage collection has actually happened.
    		KeyedWeakReference ref;
    		while ((ref = (KeyedWeakReference) queue.poll()) != null) {
    			retainedKeys.remove(ref.key);
    		}
    	}
    }
    

    ensureGone() 主要做的事情:将GC掉的对象从内存泄漏的怀疑名单中移除,如果没有会执行GC后再检查是否从怀疑名单中移除,如果没有说明内存泄漏,创建dump文件,分析dump文件。

    3.2.2 RefWatcher.watch()小结

    总结一下 RefWatcher.watch() 的处理:

    • 为每一个监听引用对象(Activity或Fragment)提供唯一ID,在主线程空闲走完生命周期时通过 Retryable.run() 检测内存泄漏

    • Retryable.run() 会调用 ensureGone() 检查,如果引用对象仍没有被GC回收仍在 ReferenceQueue 队列说明内存泄漏,创建dump文件

    3.3 HeapDumper.dumpHeap()

    通过 HeapDumper.dumpHeap() dump文件,那它是怎么dump文件的?继续分析代码:

    AndroidHeapDumper.java
    
    public final class AndroidHeapDumper implements HeapDumper {
    	@Override @Nullable
    	public File dumpHeap() {
    		File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
    		
    		if (heapDumpFile == RETRY_LATER) {
    			return RETRY_LATER;
    		}
    
    		FutureResult<Toast> waitingForToast = new FutureResult<>();
    		showToast(waitingForToast);
    
    		if (!waitingForToast.wait(5, SECONDS)) {
    			CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
    			return RETRY_LATER;
    		}
    
    		// 创建通知提示dump文件,分析进度会在启动前台分析服务时更新
    		Notification.Builder builder = new Notification.Builder(context)
    			.setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
    		Notification notification = LeakCanaryInternals.buildNotification(context, builder);
    		NotificationManager notificationManager = 
    			(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    		int notificationId = (int) SystemClock.uptimeMillis();
    		notificationManager.notify(notificationId, notification);
    
    		Toast toast = waitingForToast.get();
    		try {
    			// 最终使用的是Android提供的工具dump数据到hprof文件
    			Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
    			cancelToast(toast);
    			notificationManager.cancel(notificationId);
    		} catch (Exception e) {
    			CanaryLog.d(e, "Could not dump heap");
    			// Abort heap dump
    			return RETRY_LATER;
    		}
    	}
    }
    
    DefaultLeakDirectoryProvider.java
    
    public final class DefaultLeakDirectoryProvider implements LeakDirectoryProvider {
    	// 最多7个dump文件
    	private static final int DEFAULT_MAX_STORED_HEAP_DUMPS = 7;
    
    	private static final String HPROF_SUFFIX = ".hprof";
    	private static final String PENDING_HEAPDUMP_SUFFIX = "_pending" + HPROF_SUFFIX;
    
    	private final int maxStoredHeapDumps;
    
    	public DefaultLeakDirectoryProvider(@NonNull Context context) {
    		this(context, DEFAULT_MAX_STORED_HEAP_DUMPS);
    	}
    
    	public DefaultLeakDirectoryProvider(@NonNoll Context context, int maxStoredHeapDumps) {
    		if (maxStoredHeapDumps < 1) {
    			throw new IllegalArgumentException("maxStoredHeapDumps must be at leasts 1");
    		}
    		this.context = context.getApplicationContext();
    		this.maxStoredHeapDumps = maxStoredHeapDumps;
    	}
    
    	@Override public @Nullable File newHeapDumpFile() {
    		// 从外部存储查找对应的后缀dump文件
    		List<File> pendingHeapDumps = listFiles(new FilenameFilter() {
    			@Override public boolean accept(File dir, String filename) {
    				return filename.endsWith(PENDING_HEAPDUMP_SUFFIX);
    			}
    		});
    		
    		...
    		
    		// 查找到hprof后缀的dump文件,如果dump文件多于7个,删除修改时间较前的hprof文件
    		cleanupOldHeapDumps();
    
    		// 检查外部存储权限,如果没有存储权限LeakCanary无法dump文件
    		File storageDirectory = externalStorageDirectory();
    		if (!directoryWritableAfterMkdirs(storageDirectory)) {
    			if (!hasStoragePermission()) {
    				CanaryLog.d("WRITE_EXTERNAL_STORAGE permission not granted");
    				requestWritePermissionNotification();
    			} else {
    				String state = Environment.getExternalStorageState();
    				if (!Environment.MEDIA_MOUNTED.equals(state)) {
    					CanaryLog.d("External storage not mounted. state: %s", state);
    				} else {
    					CanaryLog.d("Could not create heap dump directory in external storage: [%s]", storageDirectory.getAbsolutePath());
    				}
    			}
    		}
    		
    		...
    		
    		return new File(storageDirectory, UUID.randomUUID().toString() + PENDING_HEAPDUMP_SUFFIX);
    	}
    }
    

    排除权限和dump文件检查外,最主要的是一句代码:Debug.dumpHprofData(),最终是由Android的工具帮助生成hprof文件。

    3.4 HeapDumpListener.analyze()

    ServiceHeapDumpListener.java
    
    public final class ServiceHeapDumpListener implements HeapDump.Listener {
    	@Override public void analyze(@NonNull HeapDump heapDump) {
        	checkNotNull(heapDump, "heapDump");
        	HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
      	}
    }
    
    HeapAnalyzerService.java
    
    // ForegroundService extends IntentService
    public final class HeapAnalyzerService extends ForegroundService
        implements AnalyzerProgressListener {
    
    	public static void runAnalysis(Context context, HeapDump heapDump,
          Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        	setEnabledBlocking(context, HeapAnalyzerService.class, true);
        	setEnabledBlocking(context, listenerServiceClass, true);
        	// 启动一个前台服务分析,跨进程通信Intent提供分析所需参数
        	Intent intent = new Intent(context, HeapAnalyzerService.class);
       	 	intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
        	intent.putExtra(HEAPDUMP_EXTRA, heapDump);
        	ContextCompat.startForegroundService(context, intent);
     	 }	
    
    	// LeakCanary是在另一个进程的,所以这里的分析也是在另一个进程
    	@Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
        	if (intent == null) {
          		CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
          		return;
        	}
        	String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
        	HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
    
        	HeapAnalyzer heapAnalyzer =
            	new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
    
    		// 开始分析
        	AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
            	heapDump.computeRetainedHeapSize);
            // 分析结果回调
        	AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
      	}
    }
    
    HeapAnalyzer.java
    
    public final class HeapAnalyzer {
    	public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
          @NonNull String referenceKey,
          boolean computeRetainedSize) {
        	...
          	// False alarm, weak reference was cleared in between key check and heap dump.
          	if (leakingRef == null) {
            	String className = leakingRef.getClassObj().getClassName();
            	return noLeak(className, since(analysisStartNanoTime)); // 没有内存泄漏
          	}
          	// 如果有内存泄漏,查找内存泄漏的引用路径,最终的分析实现LeakCanary是使用haha库实现
          	return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
        	...
      	}
    }
    

    HeapDumpListener.analyze() 其实就是启动了一个前台服务在其他进程分析内存泄漏的引用路径。

    3.5 LeakCanary源码分析总结

    在应用启动时,通过 LeakCanary.install() 监听内存泄漏,LeakCanary的处理过程如下:

    • 构建 RetWatcher 提供内存泄漏分析前的相关参数(如 DisplayService 通知服务,excludeRefs() 排除系统源码泄漏),通过 Application.registerXxxLifecycleCallback() 监听Activity或Fragment生命周期转发给 RefWatcher

    • 在Activity或Fragment回调 onDestroy() 时,监听引用对象是否还在 ReferenceQueue 中,有则表示内存泄漏,创建dump文件并通过Android工具 Debug.dumpHprofData() 写入内存泄漏数据,hprof文件将会在另一个前台服务分析

    4 LeakCanary 2.x(2.2)

    LeakCanary 1.6.x和2.x有不同的一些地方:

    • 2.x版本全部换为使用kotlin编码

    • 内存泄漏引用路径分析库由 haha 库替换为 shark 库,据说减少了90%内存占用,速度提升了6倍

    其他的基本原理都和旧版本相同。

    内部处理也有所不同:

    • 不需要自己注册 LeakCanary

    如果不需要定制配置,新版本 LeakCanary 只需要引入依赖导入即可使用。不再需要在 Application.onCreate() 手动注册 LeakCanaryLeakCanary 自动通过 ContentProvider 注册监听(ContentProvider 会在应用启动前创建):

    internal sealed class AppWatcherInstaller : ContentProvider() {
    	override fun onCreate(): Boolean {
    		val application = context!!.applicationContext as Application
    		InternalAppWatcher.install(application)
    		return true
    	}
    }
    
    • RefWatcher.watch() (kotlin不是 RefWatcher,为了体现两个版本的区别用这个名称方便理解)新旧版本处理不同:

    旧版本执行完GC后如果怀疑名单队列中还有监听的引用对象存在内存泄漏会直接dump并分析;在新版本中会存在一个阈值,如果内存泄漏的对象数量在阈值内是不会dump分析。

    internal class HeapDumpTrigger(...) {
    	private fun checkRetainedObjects(reason: String) {
    		...
    		var retainedReferenceCount = objectWatcher.retainedObjectCount
    
    		if (retainedReferenceCount > 0) {
    			gcTrigger.runGc()
    			retainedReferenceCount = objectWatcher.retainedObjectCount
    		}
    
    		// 如果内存泄漏对象数量在阈值内,不生成dump文件分析
    		if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
    		
    		...
    	}
    
    	private fun checkRetainedCount(
    		retainedKeysCount: Int,
    		retainedVisibleThreshold: Int): Boolean {
    		val countChanged = lastDisplayedRetainedObjectCount != retainedKeysCount
    		lastDisplayRetainedObjectCount = retainedKeysCount
    		if (retainedKeydsCount == 0) {
    			...
    			return true
    		}
    
    		// 引发内存泄漏的对象在阈值内,如果应用在前台会通知提示内存泄漏
    		if (retainedKeysCount < retainedVisibleThreshold) {
    			if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
    				if (countChanged) { ... }
    				// 创建通知
    				showRetainedCountNotification(...)
    				...
    				return true
    			}
    		}
    		return false
    	}
    }
    
    展开全文
  • LeakCanary原理:watch一个即将要销毁的对象 1.在Activity 调用OnDestroy()之后将它放在一个WeakReference 2.将这个WeakReference关联到一个ReferenceQueue 3.查看ReferenceQueue是否存在Activity的引用 4.如果该...

    LeakCanary原理:watch一个即将要销毁的对象
    1.在Activity 调用OnDestroy()之后将它放在一个WeakReference
    2.将这个WeakReference关联到一个ReferenceQueue
    3.查看ReferenceQueue是否存在Activity的引用
    4.如果该Activit泄漏了,Dump出heap信息,然后再去分析泄漏路径

    要了解内存泄漏首先要了解内存
    内存分为:
    1.栈(stack):存放基本类型和对象的引用
    2.堆(heap):存放new出来,创建出来的对象或者数组,在堆中的内存是由Java虚拟机的垃圾回收器来管理的,JVM只有一个堆区,同时这个堆区会被所有线程所共享
    3.方法区(method):方法区也可以称为静态区,被所有的线程共享的,方法区中还包含所有的class对象和静态变量

    为什么会产生内存泄漏
    1.对象不再需要使用,在对象需要被回收的时候,有其它正在使用的对象在引用它,导致这个本该被回收的对象不能被回收,本该被回收的对象还在堆内存中,就产生了内存泄漏
    2.有些对象只有有限的生命周期,当这些生命周期很短的对象任务完成之后,就应该垃圾回收器回收,如果在这个对象的生命周期快被结束时,还被一系列的引用,就会导致内存泄漏

    内存泄漏会导致什么问题
    随着泄漏的不断累积,可能会消耗完整个内存,导致OOM

    说到了引用,就说下引用类型
    1.强引用(StrongReference):正常使用的对象就是强引用,强引用垃圾回收器是不会去回收强引用的对象,当内存不足,虚拟机宁愿抛出OOM的错误
    2.软引用(SoftReference):内存空间足够的话,垃圾回收器不会去回收软引用的对象;但是内存空间不够的情况下,就回去回收软引用引用的对象的内存,只要垃圾回器没有回收这个软引用的对象,这个对象还是可以被使用的
    3.弱引用(WeakReference):当垃圾回收器,扫描内存的时候,这时候不去区分内存够不够,会直接回收弱引用的对象
    4.虚引用:形同虚设,不会决定一个对象的任何生命周期,垃圾回收器在任何情况下都可以回收

    ReferenceQueue
    软引用或者弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中

    看下LeakCanary.install方法

    public static @NonNull RefWatcher install(@NonNull Application application) {//RefWatcher类是用于启动ActivityRefWatcher,然后通过ActivityRefWatcher去监视Activity的回收情况
        return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
            .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
            .buildAndInstall();
      }
    

    看下buildAndInstall()方法

      public RefWatcher buildAndInstall() {
        RefWatcher refWatcher = build();//创建RefWatcher 对象
        if (refWatcher != DISABLED) {
          LeakCanary.enableDisplayLeakActivity(context);//LeakCanary就是使用的时候,弹框,告诉我们哪里有内存泄漏,enableDisplayLeakActivity开启Activity
          ActivityRefWatcher.install((Application) context, refWatcher);
        }
        return refWatcher;
      }
    

    看下 ActivityRefWatcher.install(context, refWatcher)方法

      public static void install(Application application, RefWatcher refWatcher) {
        new ActivityRefWatcher(application, refWatcher).watchActivities();
      }
      看下watchActivities()方法
       
    
    ```java
     public void watchActivities() {
        stopWatchingActivities();
        application.registerActivityLifecycleCallbacks(lifecycleCallbacks);//重新注册activity的生命周期的Callback
      }
    

    看下stopWatchingActivities()方法

    public void stopWatchingActivities() {
        application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);//反注册以前的Activity生命周期的Calback,也就是保证我们以前所做的内存泄漏的监听工作要删除
      }
    
    看下activityRefWatcher.lifecycleCallbacks源码
    
    ```java
      private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
          new ActivityLifecycleCallbacksAdapter() {
            @Override public void onActivityDestroyed(Activity activity) {
              refWatcher.watch(activity);
            }
          };
    

    看下ActivityLifecycleCallbacks

      private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
          new Application.ActivityLifecycleCallbacks() {
            @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            }
    
            @Override public void onActivityStarted(Activity activity) {
            }
    
            @Override public void onActivityResumed(Activity activity) {
            }
    
            @Override public void onActivityPaused(Activity activity) {
            }
    
            @Override public void onActivityStopped(Activity activity) {
            }
    
            @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
    
            @Override public void onActivityDestroyed(Activity activity) {
              ActivityRefWatcher.this.onActivityDestroyed(activity);//将Activity的生命周期关联到ActivityRefWatcher类当中
            }
          };
    

    看下onActivityDestroyed(activity)方法

    void onActivityDestroyed(Activity activity) {
        refWatcher.watch(activity);
      }
    

    看下RefWatcher的成员变量

    public final class RefWatcher {
    
      public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
    
      private final WatchExecutor watchExecutor;//用于去执行内存泄漏的检测用的
      private final DebuggerControl debuggerControl;//用于查询是否正在调试中,调试中就不会进行内存泄漏的检测判断
      private final GcTrigger gcTrigger;//用于处理GC的,判断内存泄漏之前,给泄漏对象一次机会,先调用一次GC,如果不GC就会把Dumper文件显示出来
      private final HeapDumper heapDumper;//Dumper出内存泄漏的堆文件
      private final Set<String> retainedKeys;//持有那些待检测的,以及那些产生内存泄漏的引用的key
      private final ReferenceQueue<Object> queue;//引用队列,主要是判断弱引用所持有的对象,是否已经被执行了GC垃圾回收
      private final HeapDump.Listener heapdumpListener;//主要是用于分析一些产生Heap文件的回调
      private final ExcludedRefs excludedRefs;//排除一些系统的bug引起的内存泄漏
                                          .
                                          .
                                          .
      }
    

    看下 refWatcher.watch(activity)方法

     public void watch(Object watchedReference) {
        watch(watchedReference, "");
      }         
    

    看下 watch(watchedReference, “”)方法

    public void watch(Object watchedReference, String referenceName) {
        if (this == DISABLED) {
          return;
        }
        checkNotNull(watchedReference, "watchedReference");
        checkNotNull(referenceName, "referenceName");
        final long watchStartNanoTime = System.nanoTime();
        String key = UUID.randomUUID().toString();//对Refenerence添加一个唯一的key值引用
        retainedKeys.add(key);//将唯一的key添加到前面说过的set集合当中
        final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);//创建所需要的弱引用对象
    
        ensureGoneAsync(watchStartNanoTime, reference);//开启一个异步线程来分析,刚才所创建好的KeyedWeakReference
      }
    

    看下ensureGoneAsync(watchStartNanoTime, reference)方法

     private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
        watchExecutor.execute(new Retryable() {
          @Override public Retryable.Result run() {
            return ensureGone(reference, watchStartNanoTime);
          }
        });
      }
    

    看下ensureGone(reference, watchStartNanoTime)方法

    Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {//用来确保Activity是否已经进入Gone状态,Gone表示Activity是否真的已经被回收了
        long gcStartNanoTime = System.nanoTime();
        long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    
        removeWeaklyReachableReferences();
    
        if (debuggerControl.isDebuggerAttached()) {//判断如果是出于处于调试状态的时候,就不会进行内存泄漏的分析
          return RETRY;
        }
        if (gone(reference)) {//如果当前对象已经可达,已经不会造成内存泄漏,这就是不属于内存泄漏的范围
          return DONE;
        }
        gcTrigger.runGc();//gcTrigger进行垃圾回收的对象,gcTrigger.runGc()如果当前对象没有改变它的可达状态,进行手动的垃圾回收
        removeWeaklyReachableReferences();//清除已经到达引用队列的弱引用,把已经回收的对象的Key,从Set对象中移除,剩下的Key保证都是未被回收的
        if (!gone(reference)) {//表示已经出现内存泄漏
          long startDumpHeap = System.nanoTime();
          long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);//用于计算调用watch这个方法到我们调用了GC垃圾回收总共所产生的时间
          File heapDumpFile = heapDumper.dumpHeap();//Dumper出我们所需要的内存泄漏文件
          if (heapDumpFile == RETRY_LATER) {
            // Could not dump the heap.
            return RETRY;
          }
          long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
          heapdumpListener.analyze( //  heapdumpListener.analyze去真正分析内存泄漏,以及它的路径
              new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
                  gcDurationMs, heapDumpDurationMs));
        }
        return DONE;
      }
    

    总结下:
    1.首先会创建一个ActivityRefWatcher,启动一个ActivityRefWatcher
    2.通过ActivityLifecycleCallbacks把activity的OnDestory生命周期关联
    3.最后在线程池中去开始分析内存泄漏

    看下 heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs));这个抽象方法的实现

     @Override public void analyze(HeapDump heapDump) {
        checkNotNull(heapDump, "heapDump");
        HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
      }      
    

    看下 HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);方法

    public final class HeapAnalyzerService extends IntentService {//是个IntentService,最后会到 onHandleIntent(Intent intent)方法
    
      private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
      private static final String HEAPDUMP_EXTRA = "heapdump_extra";
    
      public static void runAnalysis(Context context, HeapDump heapDump,
          Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        Intent intent = new Intent(context, HeapAnalyzerService.class);
        intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
        intent.putExtra(HEAPDUMP_EXTRA, heapDump);
        context.startService(intent);
      }
    
      public HeapAnalyzerService() {
        super(HeapAnalyzerService.class.getSimpleName());
      }
    
      @Override protected void onHandleIntent(Intent intent) {
        if (intent == null) {
          CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
          return;
        }
        String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);//获取runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass)方法传进来的参数
        HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
    
        HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);//用于分析堆内存的,heapDump.excludedRefs排除系统Bug所引起的内存泄漏
    
        AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);//分析内存使用的结果
        AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);//回调进行结果的显示
      }
    }
    

    看下heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey)方法

      public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
        long analysisStartNanoTime = System.nanoTime();
    
        if (!heapDumpFile.exists()) {
          Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
          return failure(exception, since(analysisStartNanoTime));
        }
    
        try {
          HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
          HprofParser parser = new HprofParser(buffer);//解析器
          Snapshot snapshot = parser.parse();//具体的解析工作
          deduplicateGcRoots(snapshot);//去重的GCRoot,就是会对分析的结果,去除重复的内存泄漏
    
          Instance leakingRef = findLeakingReference(referenceKey, snapshot);//根据需要检测的类Key,来查询检测结果中是否我们需要的对象
    
          // False alarm, weak reference was cleared in between key check and heap dump.
          if (leakingRef == null) {//表示GC后,对象已经被清除了
            return noLeak(since(analysisStartNanoTime));
          }
    
          return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);//有泄漏,找出整个路径
        } catch (Throwable e) {
          return failure(e, since(analysisStartNanoTime));
        }
      }
    

    总结 checkForLeak(最重要的方法)
    1.把.hprof转化为Snapshot
    2.优化GCRoots
    3.找出泄漏的对象/找出泄漏对象的最短路径

    看下findLeakingReference(referenceKey, snapshot)方法

    private Instance findLeakingReference(String key, Snapshot snapshot) {
        ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());//创建Class对象,通过KeyedWeakReference弱引用就可以查找出内存泄漏的对象
        List<String> keysFound = new ArrayList<>();
        for (Instance instance : refClass.getInstancesList()) {
          List<ClassInstance.FieldValue> values = classInstanceValues(instance);
          String keyCandidate = asString(fieldValue(values, "key"));
          if (keyCandidate.equals(key)) {//找到了检测对象
            return fieldValue(values, "referent");
          }
          keysFound.add(keyCandidate);//添加找到的值
        }
        throw new IllegalStateException(
            "Could not find weak reference with key " + key + " in " + keysFound);
      }
    

    总结findLeakingReference方法
    1.在Snapshot 快照中找到第一个弱引用(因为对象没有被回收,所以第一个对象就代表内存泄漏)
    2.遍历这个对象的所有势力
    3.如果Key值和最开始定义封装的Key值相同,那么返回这个泄漏对象

    看下findLeakTrace(analysisStartNanoTime, snapshot, leakingRef)方法

    private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
          Instance leakingRef) {//根据泄漏对象的引用,找到的对象,找出最短的路径
    
        ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
        ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);//这两行是判断内存泄漏的关键,通过分析hprof文件,找到内存泄漏的点,判断的依据就是GCRoot,GCRoot表示不能被垃圾回收器GC回收的对象。这里我们关注两种类型,第一种静态的;第二中被其它线程所引用,并且其它线程正在运行,没有结束
    
        // False alarm, no strong reference path to GC Roots.
        if (result.leakingNode == null) {
          return noLeak(since(analysisStartNanoTime));
        }
    
        LeakTrace leakTrace = buildLeakTrace(result.leakingNode);//生成的内存泄漏的调用栈,如果发生内存泄漏,展示在你的屏幕上的就是这个LeakTrace
    
        String className = leakingRef.getClassObj().getClassName();
    
        // Side effect: computes retained size.
        snapshot.computeDominators();
    
        Instance leakingInstance = result.leakingNode.instance;
    
        long retainedSize = leakingInstance.getTotalRetainedSize();//计算内存泄漏的空间大小
    
        //  check O sources and see what happened to android.graphics.Bitmap.mBuffer
        if (SDK_INT <= N_MR1) {
          retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
        }
    
        return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
            since(analysisStartNanoTime));
      }
    

    总结findLeakTrace方法
    1.解析Hprof文件,把和这个文件封装成Snopshot
    2.根据弱引用和前面定义的Key值,确定泄漏的对象
    3.找到最短的泄漏路径,作为结果反馈出来 (如果找不到,可能是误判或者被垃圾回收器回收了)

    展开全文
  • LeakCanary 源码解析

    2018-03-05 13:32:47
    LeakCanary 是由 Square 开源的针对 Android 和 Java 的内存泄漏检测工具。 使用 LeakCanary 的集成过程很简单,首先在 build.gradle 文件中添加依赖: dependencies { debugImplementation '...

    LeakCanary 是由 Square 开源的针对 AndroidJava 的内存泄漏检测工具。

    使用

    LeakCanary 的集成过程很简单,首先在 build.gradle 文件中添加依赖:

    dependencies {
      debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
      releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
    }
    复制代码

    debugrelease 版本中使用的是不同的库。LeakCanary 运行时会经常执行 GC 操作,在 release 版本中会影响效率。android-no-op 版本中基本没有逻辑实现,用于 release 版本。

    然后实现自己的 Application 类:

    public class ExampleApplication extends Application {
    
      @Override public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {
          // This process is dedicated to LeakCanary for heap analysis.
          // You should not init your app in this process.
          return;
        }
        LeakCanary.install(this);
        // Normal app init code...
      }
    }
    复制代码

    这样就集成完成了。当 LeakCanary 检测到内存泄露时,会自动弹出 Notification 通知开发者发生内存泄漏的 Activity 和引用链,以便进行修复。

    源码分析

    从入口函数 LeakCanary.install(this) 开始分析:

    LeakCanary.install

    LeakCanary.java

    /**
     * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
     * references (on ICS+).
     */
    public static RefWatcher install(Application application) {
      return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
          .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
          .buildAndInstall();
    }
    复制代码

    LeakCanary.refWatcher

    LeakCanary.java

    /** Builder to create a customized {@link RefWatcher} with appropriate Android defaults. */
    public static AndroidRefWatcherBuilder refWatcher(Context context) {
      return new AndroidRefWatcherBuilder(context);
    }
    复制代码

    refWatcher() 方法新建了一个 AndroidRefWatcherBuilder 对象,该对象继承于 RefWatcherBuilder 类,配置了一些默认参数,利用建造者构建一个 RefWatcher 对象。

    AndroidRefWatcherBuilder.listenerServiceClass

    AndroidRefWatcherBuilder.java

    public AndroidRefWatcherBuilder listenerServiceClass(
        Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
      return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
    }
    复制代码

    RefWatcherBuilder.java

    /** @see HeapDump.Listener */
    public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
      this.heapDumpListener = heapDumpListener;
      return self();
    }
    复制代码

    DisplayLeakService.java

    /**
     * Logs leak analysis results, and then shows a notification which will start {@link
     * DisplayLeakActivity}.
     *
     * You can extend this class and override {@link #afterDefaultHandling(HeapDump, AnalysisResult,
     * String)} to add custom behavior, e.g. uploading the heap dump.
     */
    public class DisplayLeakService extends AbstractAnalysisResultService {}
    复制代码

    listenerServiceClass() 方法绑定了一个后台服务 DisplayLeakService,这个服务主要用来分析内存泄漏结果并发送通知。你可以继承并重写这个类来进行一些自定义操作,比如上传分析结果等。

    RefWatcherBuilder.excludedRefs

    RefWatcherBuilder.java

    public final T excludedRefs(ExcludedRefs excludedRefs) {
      this.excludedRefs = excludedRefs;
      return self();
    }
    复制代码

    AndroidExcludedRefs.java

    /**
     * This returns the references in the leak path that can be ignored for app developers. This
     * doesn't mean there is no memory leak, to the contrary. However, some leaks are caused by bugs
     * in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app
     * developers except by resorting to serious hacks, so we remove the noise caused by those leaks.
     */
    public static ExcludedRefs.Builder createAppDefaults() {
      return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
    }
    
    public static ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) {
      ExcludedRefs.Builder excluded = ExcludedRefs.builder();
      for (AndroidExcludedRefs ref : refs) {
        if (ref.applies) {
          ref.add(excluded);
          ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
        }
      }
      return excluded;
    }
    复制代码

    excludedRefs() 方法定义了一些对于开发者可以忽略的路径,意思就是即使这里发生了内存泄漏,LeakCanary 也不会弹出通知。这大多是系统 Bug 导致的,无需用户进行处理。

    AndroidRefWatcherBuilder.buildAndInstall

    最后调用 buildAndInstall() 方法构建 RefWatcher 实例并开始监听 Activity 的引用:

    AndroidRefWatcherBuilder.java

    /**
     * Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
     */
    public RefWatcher buildAndInstall() {
      RefWatcher refWatcher = build();
      if (refWatcher != DISABLED) {
        LeakCanary.enableDisplayLeakActivity(context);
        ActivityRefWatcher.install((Application) context, refWatcher);
      }
      return refWatcher;
    }
    复制代码

    看一下主要的 build()install() 方法:

    RefWatcherBuilder.build

    RefWatcherBuilder.java

    /** Creates a {@link RefWatcher}. */
     public final RefWatcher build() {
       if (isDisabled()) {
         return RefWatcher.DISABLED;
       }
    
       ExcludedRefs excludedRefs = this.excludedRefs;
       if (excludedRefs == null) {
         excludedRefs = defaultExcludedRefs();
       }
    
       HeapDump.Listener heapDumpListener = this.heapDumpListener;
       if (heapDumpListener == null) {
         heapDumpListener = defaultHeapDumpListener();
       }
    
       DebuggerControl debuggerControl = this.debuggerControl;
       if (debuggerControl == null) {
         debuggerControl = defaultDebuggerControl();
       }
    
       HeapDumper heapDumper = this.heapDumper;
       if (heapDumper == null) {
         heapDumper = defaultHeapDumper();
       }
    
       WatchExecutor watchExecutor = this.watchExecutor;
       if (watchExecutor == null) {
         watchExecutor = defaultWatchExecutor();
       }
    
       GcTrigger gcTrigger = this.gcTrigger;
       if (gcTrigger == null) {
         gcTrigger = defaultGcTrigger();
       }
    
       return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
               excludedRefs);
     }
    复制代码

    build() 方法利用建造者模式构建 RefWatcher 实例,看一下其中的主要参数:

    • watchExecutor : 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
    • debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测
    • gcTrigger : 用于 GCwatchExecutor 首次检测到可能的内存泄漏,会主动进行 GCGC 之后会再检测一次,仍然泄漏的判定为内存泄漏,进行后续操作
    • heapDumper : dump 内存泄漏处的 heap 信息,写入 hprof 文件
    • heapDumpListener : 解析完 hprof 文件并通知 DisplayLeakService 弹出提醒
    • excludedRefs : 排除可以忽略的泄漏路径

    LeakCanary.enableDisplayLeakActivity

    接下来就是最核心的 install() 方法,这里就开始观察 Activity 的引用了。在这之前还执行了一步操作,LeakCanary.enableDisplayLeakActivity(context);

    public static void enableDisplayLeakActivity(Context context) {
      setEnabled(context, DisplayLeakActivity.class, true);
    }
    复制代码

    最后执行到 LeakCanaryInternals#setEnabledBlocking

    public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
        boolean enabled) {
      ComponentName component = new ComponentName(appContext, componentClass);
      PackageManager packageManager = appContext.getPackageManager();
      int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
      // Blocks on IPC.
      packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
    }
    复制代码

    这里启用了 DisplayLeakActivity 并且显示应用图标。注意,这是指的不是你自己的应用图标,是一个单独的 LeakCanary 的应用,用于展示内存泄露历史的,入口函数是 DisplayLeakActivity,在 AndroidManifest.xml 中可以看到默认情况下 android:enabled="false" :

    <activity
        android:theme="@style/leak_canary_LeakCanary.Base"
        android:name=".internal.DisplayLeakActivity"
        android:process=":leakcanary"
        android:enabled="false"
        android:label="@string/leak_canary_display_activity_label"
        android:icon="@mipmap/leak_canary_icon"
        android:taskAffinity="com.squareup.leakcanary.${applicationId}"
        >
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
    复制代码

    ActivityRefWatcher.install

    ActivityRefWatcher.java

    public static void install(Application application, RefWatcher refWatcher) {
      new ActivityRefWatcher(application, refWatcher).watchActivities();
    }
    
    public void watchActivities() {
      // Make sure you don't get installed twice.
      stopWatchingActivities();
      application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
    }
    复制代码

    watchActivities() 方法中先解绑生命周期回调注册 lifecycleCallbacks,再重新绑定,避免重复绑定。lifecycleCallbacks 监听了 Activity 的各个生命周期,在 onDestroy() 中开始检测当前 Activity 的引用。

    private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
        new Application.ActivityLifecycleCallbacks() {
          @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
          }
    
          @Override public void onActivityStarted(Activity activity) {
          }
    
          @Override public void onActivityResumed(Activity activity) {
          }
    
          @Override public void onActivityPaused(Activity activity) {
          }
    
          @Override public void onActivityStopped(Activity activity) {
          }
    
          @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
          }
    
          @Override public void onActivityDestroyed(Activity activity) {
            ActivityRefWatcher.this.onActivityDestroyed(activity);
          }
        };
    
        void onActivityDestroyed(Activity activity) {
          refWatcher.watch(activity);
        }
    复制代码

    下面着重分析 RefWatcher 是如何检测 Activity 的。

    RefWatcher.watch

    调用 RefWatcher#watch 检测 ActivityRefWatcher.java

    /**
     * Identical to {@link #watch(Object, String)} with an empty string reference name.
     *
     * @see #watch(Object, String)
     */
    public void watch(Object watchedReference) {
      watch(watchedReference, "");
    }
    
    /**
     * Watches the provided references and checks if it can be GCed. This method is non blocking,
     * the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
     * with.
     *
     * @param referenceName An logical identifier for the watched object.
     */
    public void watch(Object watchedReference, String referenceName) {
      if (this == DISABLED) {
        return;
      }
      checkNotNull(watchedReference, "watchedReference");
      checkNotNull(referenceName, "referenceName");
      final long watchStartNanoTime = System.nanoTime();
      String key = UUID.randomUUID().toString();
      retainedKeys.add(key);
      final KeyedWeakReference reference =
          new KeyedWeakReference(watchedReference, key, referenceName, queue);
    
      ensureGoneAsync(watchStartNanoTime, reference);
    }
    复制代码

    watch() 方法的参数是 ObjectLeakCanary 并不仅仅是针对 Android 的,它可以检测任何对象的内存泄漏,原理都是一致的。

    这里出现了几个新面孔,先来了解一下各自是什么:

    • retainedKeys : 一个 Set<String> 集合,每个检测的对象都对应着一个唯一的 key,存储在 retainedKeys
    • KeyedWeakReference : 自定义的弱引用,持有检测对象和对用的 key
    final class KeyedWeakReference extends WeakReference<Object> {
      public final String key;
      public final String name;
    
      KeyedWeakReference(Object referent, String key, String name,
          ReferenceQueue<Object> referenceQueue) {
        super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
        this.key = checkNotNull(key, "key");
        this.name = checkNotNull(name, "name");
      }
    }
    复制代码
    • queue : ReferenceQueue 对象,和 KeyedWeakReference 配合使用

    这里有个小知识点,弱引用和引用队列 ReferenceQueue 联合使用时,如果弱引用持有的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。即 KeyedWeakReference 持有的 Activity 对象如果被垃圾回收,该对象就会加入到引用队列 queue 中。

    接着看看具体的内存泄漏判断过程:

    RefWatcher.ensureGoneAsync

    private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
      watchExecutor.execute(new Retryable() {
        @Override public Retryable.Result run() {
          return ensureGone(reference, watchStartNanoTime);
        }
      });
    }
    复制代码

    通过 watchExecutor 执行检测操作,这里的 watchExecutorAndroidWatchExecutor 对象。

    @Override protected WatchExecutor defaultWatchExecutor() {
      return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
    }
    复制代码

    DEFAULT_WATCH_DELAY_MILLIS 为 5 s。

    public AndroidWatchExecutor(long initialDelayMillis) {
      mainHandler = new Handler(Looper.getMainLooper());
      HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
      handlerThread.start();
      backgroundHandler = new Handler(handlerThread.getLooper());
      this.initialDelayMillis = initialDelayMillis;
      maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
    }
    复制代码

    看看其中用到的几个对象:

    • mainHandler : 主线程消息队列
    • handlerThread : 后台线程,HandlerThread 对象,线程名为 LeakCanary-Heap-Dump
    • backgroundHandler : 上面的后台线程的消息队列
    • initialDelayMillis : 5 s,即之前的 DEFAULT_WATCH_DELAY_MILLIS
    @Override public void execute(Retryable retryable) {
      if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
        waitForIdle(retryable, 0);
      } else {
        postWaitForIdle(retryable, 0);
      }
    }
    
    void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
      mainHandler.post(new Runnable() {
        @Override public void run() {
          waitForIdle(retryable, failedAttempts);
        }
      });
    }
    
    void waitForIdle(final Retryable retryable, final int failedAttempts) {
      // This needs to be called from the main thread.
      Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override public boolean queueIdle() {
          postToBackgroundWithDelay(retryable, failedAttempts);
          return false;
        }
      });
    }
    复制代码

    在具体的 execute() 过程中,不管是 waitForIdle 还是 postWaitForIdle,最终还是要切换到主线程中执行。要注意的是,这里的 IdleHandler 到底是什么时候去执行?

    我们都知道 Handler 是循环处理 MessageQueue 中的消息的,当消息队列中没有更多消息需要处理的时候,且声明了 IdleHandler 接口,这是就会去处理这里的操作。即指定一些操作,当线程空闲的时候来处理。当主线程空闲时,就会通知后台线程延时 5 秒执行内存泄漏检测工作。

    void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
      long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
      long delayMillis = initialDelayMillis * exponentialBackoffFactor;
      backgroundHandler.postDelayed(new Runnable() {
        @Override public void run() {
          Retryable.Result result = retryable.run();
          if (result == RETRY) {
            postWaitForIdle(retryable, failedAttempts + 1);
          }
        }
      }, delayMillis);
    }
    复制代码

    下面是真正的检测过程,AndroidWatchExecutor 在执行时调用 ensureGone() 方法:

    RefWatcher.ensureGone

    Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
      long gcStartNanoTime = System.nanoTime();
      long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    
      removeWeaklyReachableReferences();
    
      if (debuggerControl.isDebuggerAttached()) {
        // The debugger can create false leaks.
        return RETRY;
      }
      if (gone(reference)) {
        return DONE;
      }
      gcTrigger.runGc();
      removeWeaklyReachableReferences();
      if (!gone(reference)) {
        long startDumpHeap = System.nanoTime();
        long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
    
        File heapDumpFile = heapDumper.dumpHeap();
        if (heapDumpFile == RETRY_LATER) {
          // Could not dump the heap.
          return RETRY;
        }
        long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
        heapdumpListener.analyze(
            new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
                gcDurationMs, heapDumpDurationMs));
      }
      return DONE;
    }
    复制代码

    再重复一次几个变量的含义,retainedKeys 是一个 Set集合,存储检测对象对应的唯一 key 值,queue是一个引用队列,存储被垃圾回收的对象。

    主要过程有一下几步:

    RefWatcher.emoveWeaklyReachableReferences()

    private void removeWeaklyReachableReferences() {
      // 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.
      KeyedWeakReference ref;
      while ((ref = (KeyedWeakReference) queue.poll()) != null) {
        retainedKeys.remove(ref.key);
      }
    }
    复制代码

    遍历引用队列 queue,判断队列中是否存在当前 Activity 的弱引用,存在则删除 retainedKeys 中对应的引用的 key值。

    RefWatcher.gone()

    private boolean gone(KeyedWeakReference reference) {
      return !retainedKeys.contains(reference.key);
    }
    复制代码

    判断 retainedKeys 中是否包含当前 Activity 引用的 key 值。

    如果不包含,说明上一步操作中 retainedKeys 移除了该引用的 key 值,也就说上一步操作之前引用队列 queue 中包含该引用,GC 处理了该引用,未发生内存泄漏,返回 DONE,不再往下执行。

    如果包含,并不会立即判定发生内存泄漏,可能存在某个对象已经不可达,但是尚未进入引用队列 queue。这时会主动执行一次 GC 操作之后再次进行判断。

    gcTrigger.runGc()

    /**
     * Called when a watched reference is expected to be weakly reachable, but hasn't been enqueued
     * in the reference queue yet. This gives the application a hook to run the GC before the {@link
     * RefWatcher} checks the reference queue again, to avoid taking a heap dump if possible.
     */
    public interface GcTrigger {
      GcTrigger DEFAULT = new GcTrigger() {
        @Override public void runGc() {
          // Code taken from AOSP FinalizationTest:
          // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
          // java/lang/ref/FinalizationTester.java
          // System.gc() does not garbage collect every time. Runtime.gc() is
          // more likely to perfom a gc.
          Runtime.getRuntime().gc();
          enqueueReferences();
          System.runFinalization();
        }
    
        private void enqueueReferences() {
          // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
          // references to the appropriate queues.
          try {
            Thread.sleep(100);
          } catch (InterruptedException e) {
            throw new AssertionError();
          }
        }
      };
    
      void runGc();
    }
    复制代码

    注意这里调用 GC 的写法,并不是使用 System.gcSystem.gc 仅仅只是通知系统在合适的时间进行一次垃圾回收操作,实际上并不能保证一定执行。

    主动进行 GC 之后会再次进行判定,过程同上。首先调用 removeWeaklyReachableReferences() 清除 retainedKeys 中弱引用的 key 值,再判断是否移除。如果仍然没有移除,判定为内存泄漏。

    内存泄露结果处理

    AndroidHeapDumper.dumpHeap

    判定内存泄漏之后,调用 heapDumper.dumpHeap() 进行处理:

    AndroidHeapDumper.java

    @SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
    @Override public File dumpHeap() {
      File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
    
      if (heapDumpFile == RETRY_LATER) {
        return RETRY_LATER;
      }
    
      FutureResult<Toast> waitingForToast = new FutureResult<>();
      showToast(waitingForToast);
    
      if (!waitingForToast.wait(5, SECONDS)) {
        CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
        return RETRY_LATER;
      }
    
      Toast toast = waitingForToast.get();
      try {
        Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
        cancelToast(toast);
        return heapDumpFile;
      } catch (Exception e) {
        CanaryLog.d(e, "Could not dump heap");
        // Abort heap dump
        return RETRY_LATER;
      }
    }
    复制代码

    leakDirectoryProvider.newHeapDumpFile() 新建了 hprof 文件,然后调用 Debug.dumpHprofData() 方法 dump 当前堆内存并写入刚才创建的文件。

    回到 RefWatcher.ensureGone() 方法中,生成 heapDumpFile 文件之后,通过 heapdumpListener 分析。

    ServiceHeapDumpListener.analyze

    heapdumpListener.analyze(
              new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
                  gcDurationMs, heapDumpDurationMs));
    复制代码

    这里的 heapdumpListenerServiceHeapDumpListener 对象,接着进入 ServiceHeapDumpListener.runAnalysis() 方法。

    @Override public void analyze(HeapDump heapDump) {
      checkNotNull(heapDump, "heapDump");
      HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
    }
    复制代码

    这里的 listenerServiceClass 指的是 DisplayLeakService.class,文章开头提到的 AndroidRefWatcherBuilder 中进行了配置。

    @Override protected HeapDump.Listener defaultHeapDumpListener() {
      return new ServiceHeapDumpListener(context, DisplayLeakService.class);
    }
    复制代码

    HeapAnalyzerService.runAnalysis

    HeapAnalyzerService.runAnalysis() 方法中启动了它自己,传递了两个参数,DisplayLeakService 类名和要分析的 heapDump。启动自己后,在 onHandleIntent 中进行处理。

    /**
     * This service runs in a separate process to avoid slowing down the app process or making it run
     * out of memory.
     */
    public final class HeapAnalyzerService extends IntentService {
    
      private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
      private static final String HEAPDUMP_EXTRA = "heapdump_extra";
    
      public static void runAnalysis(Context context, HeapDump heapDump,
          Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        Intent intent = new Intent(context, HeapAnalyzerService.class);
        intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
        intent.putExtra(HEAPDUMP_EXTRA, heapDump);
        context.startService(intent);
      }
    
      public HeapAnalyzerService() {
        super(HeapAnalyzerService.class.getSimpleName());
      }
    
      @Override protected void onHandleIntent(Intent intent) {
        if (intent == null) {
          CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
          return;
        }
        String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
        HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
    
        HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);
    
        AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
        AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
      }
    }
    复制代码

    heapAnalyzer.checkForLeak

    checkForLeak 方法中主要使用了 Square 公司的另一个库 haha 来分析 Android heap dump,得到结果后回调给 DisplayLeakService

    AbstractAnalysisResultService.sendResultToListener

    public static void sendResultToListener(Context context, String listenerServiceClassName,
        HeapDump heapDump, AnalysisResult result) {
      Class<?> listenerServiceClass;
      try {
        listenerServiceClass = Class.forName(listenerServiceClassName);
      } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
      }
      Intent intent = new Intent(context, listenerServiceClass);
      intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
      intent.putExtra(RESULT_EXTRA, result);
      context.startService(intent);
    }
    复制代码

    同样在 onHandleIntent 中进行处理。

    DisplayLeakService.onHandleIntent

    @Override protected final void onHandleIntent(Intent intent) {
      HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
      AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
      try {
        onHeapAnalyzed(heapDump, result);
      } finally {
        //noinspection ResultOfMethodCallIgnored
        heapDump.heapDumpFile.delete();
      }
    }
    复制代码

    DisplayLeakService.onHeapAnalyzed

    调用 onHeapAnalyzed() 之后,会将 hprof 文件删除。

    DisplayLeakService.java

    @Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
      String leakInfo = leakInfo(this, heapDump, result, true);
      CanaryLog.d("%s", leakInfo);
    
      boolean resultSaved = false;
      boolean shouldSaveResult = result.leakFound || result.failure != null;
      if (shouldSaveResult) {
        heapDump = renameHeapdump(heapDump);
        resultSaved = saveResult(heapDump, result);
      }
    
      PendingIntent pendingIntent;
      String contentTitle;
      String contentText;
    
      if (!shouldSaveResult) {
        contentTitle = getString(R.string.leak_canary_no_leak_title);
        contentText = getString(R.string.leak_canary_no_leak_text);
        pendingIntent = null;
      } else if (resultSaved) {
        pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
    
        if (result.failure == null) {
          String size = formatShortFileSize(this, result.retainedHeapSize);
          String className = classSimpleName(result.className);
          if (result.excludedLeak) {
            contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
          } else {
            contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
          }
        } else {
          contentTitle = getString(R.string.leak_canary_analysis_failed);
        }
        contentText = getString(R.string.leak_canary_notification_message);
      } else {
        contentTitle = getString(R.string.leak_canary_could_not_save_title);
        contentText = getString(R.string.leak_canary_could_not_save_text);
        pendingIntent = null;
      }
      // New notification id every second.
      int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
      showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
      afterDefaultHandling(heapDump, result, leakInfo);
    }
    复制代码

    根据分析结果,调用 showNotification() 方法构建了一个 Notification 向开发者通知内存泄漏。

    public static void showNotification(Context context, CharSequence contentTitle,
        CharSequence contentText, PendingIntent pendingIntent, int notificationId) {
      NotificationManager notificationManager =
          (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    
      Notification notification;
      Notification.Builder builder = new Notification.Builder(context) //
          .setSmallIcon(R.drawable.leak_canary_notification)
          .setWhen(System.currentTimeMillis())
          .setContentTitle(contentTitle)
          .setContentText(contentText)
          .setAutoCancel(true)
          .setContentIntent(pendingIntent);
      if (SDK_INT >= O) {
        String channelName = context.getString(R.string.leak_canary_notification_channel);
        setupNotificationChannel(channelName, notificationManager, builder);
      }
      if (SDK_INT < JELLY_BEAN) {
        notification = builder.getNotification();
      } else {
        notification = builder.build();
      }
      notificationManager.notify(notificationId, notification);
    }
    复制代码

    DisplayLeakService.afterDefaultHandling

    最后还会执行一个空实现的方法 afterDefaultHandling

    /**
     * You can override this method and do a blocking call to a server to upload the leak trace and
     * the heap dump. Don't forget to check {@link AnalysisResult#leakFound} and {@link
     * AnalysisResult#excludedLeak} first.
     */
    protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
    }
    复制代码

    你可以重写这个方法进行一些自定义的操作,比如向服务器上传泄漏的堆栈信息等。

    这样,LeakCanary 就完成了整个内存泄漏检测的过程。可以看到,LeakCanary 的设计思路十分巧妙,同时也很清晰,有很多有意思的知识点,像对于弱引用和 ReferenceQueue 的使用, IdleHandler 的使用,四大组件的开启和关闭等等,都很值的大家去深究。

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,791
精华内容 1,116
关键字:

leakcanary源码解析