在C/C++语言开发过程中,比如C语言 malloc
分配内存 free
释放内存,C++是 new Object
分配内存 delete object
释放内存,对象的内存分配回收都需要程序员下意识的去维护,否则很容易出现内存泄漏。
但在Java中却没有这种情况,我们使用Java开发很多时候只需要 new Object
创建对象,使用对象后的无效内存都让GC回收了。但也存在GC无法回收的情况导致JVM内存泄漏:长周期的对象引用一直持有着短周期的对象引用,GC以为短周期对象引用还被使用,GC没有回收,所以导致了内存泄漏。
那么,Java是怎么判断对象的引用还被使用?Java垃圾回收机制是怎样的?
引用计数法
简单理解就是记录一个对象被引用的次数,一个引用被使用引用计数器就+1,反之就-1,当引用次数为0就说明是一个垃圾对象可以被回收了。
引用计数法实现非常简单,但也有存在的问题:循环引用,即对象a和对象b各自持有双方的引用,导致GC无法回收,也就导致内存泄漏。
可达性分析法
根据是否被GC Root引用确认是否是垃圾对象要被GC回收。
可以作为GC Root的对象有:
在线程栈中的局部变量(即正在被调用的方法里面的参数和局部变量)
存活的线程对象
JNI的引用
Class对象(在Android中Class被加载后是不会被卸载的)
引用类型的静态变量
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);
}
}
});
总结一下我们调用 LeakCanary.install()
的处理:
创建 AndroidRefWatcherBuilder
构建 LeakCanary
所需参数,如提供 DisplayLeakService
内存泄漏通知服务,排除Android系统源码出现的内存泄漏
创建 RefWatcher
,关联app包名生成 LeakCanary
应用,通过 registerXxxLifecycleCallbacks()
监听生命周期转发给 RefWatcher
处理
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);
}
}
watchExecutor
是 AndroidWatchExecutor
,在 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文件。
总结一下 RefWatcher.watch()
的处理:
为每一个监听引用对象(Activity或Fragment)提供唯一ID,在主线程空闲走完生命周期时通过 Retryable.run()
检测内存泄漏
Retryable.run()
会调用 ensureGone()
检查,如果引用对象仍没有被GC回收仍在 ReferenceQueue
队列说明内存泄漏,创建dump文件
通过 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文件。
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()
其实就是启动了一个前台服务在其他进程分析内存泄漏的引用路径。
在应用启动时,通过 LeakCanary.install()
监听内存泄漏,LeakCanary的处理过程如下:
构建 RetWatcher
提供内存泄漏分析前的相关参数(如 DisplayService
通知服务,excludeRefs()
排除系统源码泄漏),通过 Application.registerXxxLifecycleCallback()
监听Activity或Fragment生命周期转发给 RefWatcher
在Activity或Fragment回调 onDestroy()
时,监听引用对象是否还在 ReferenceQueue
中,有则表示内存泄漏,创建dump文件并通过Android工具 Debug.dumpHprofData()
写入内存泄漏数据,hprof文件将会在另一个前台服务分析
LeakCanary
1.6.x和2.x有不同的一些地方:
2.x版本全部换为使用kotlin编码
内存泄漏引用路径分析库由 haha
库替换为 shark
库,据说减少了90%内存占用,速度提升了6倍
其他的基本原理都和旧版本相同。
内部处理也有所不同:
LeakCanary
如果不需要定制配置,新版本 LeakCanary
只需要引入依赖导入即可使用。不再需要在 Application.onCreate()
手动注册 LeakCanary
,LeakCanary
自动通过 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
}
}