LeakCanary源码分析以及ContentProvider的优化方案

然后我们可以看到LeakSentryInstaller这个类到底做了什么

internal class LeakSentryInstaller : ContentProvider() {

override fun onCreate(): Boolean {
CanaryLog.logger = DefaultCanaryLog()
val application = context!!.applicationContext as Application
//利用系统自动调用ContentProvider的onCreate来进行安装
InternalLeakSentry.install(application)
return true
}

至于为什么系统会调用ContentProvider的onCreate方法,我们可以看看源码,在ActivityThread中的H中的handleMessage可以看到

public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “bindApplication”);
AppBindData data = (AppBindData)msg.obj;
//关键方法
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;

然后在handleBindApplication中可以看到

// don’t bring up providers in restricted mode; they may depend on the
// app’s custom Application class
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//contentprovider初始化,里面会调用onCreate方法
installContentProviders(app, data.providers);
}
}

// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don’t want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "

  • data.instrumentationName + ": " + e.toString(), e);
    }
    try {
    //app的onCreate方法调用
    mInstrumentation.callApplicationOnCreate(app);
    } catch (Exception e) {

具体调用contentprovider的onCreate代码逻辑如下

@UnsupportedAppUsage
private void installContentProviders(
Context context, List providers) {
final ArrayList results = new ArrayList<>();

for (ProviderInfo cpi : providers) {
···
//installProvider方法
ContentProviderHolder cph = installProvider(context, null, cpi,
false /noisy/, true /noReleaseNeeded/, true /stable/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
//installProvider方法,然后一步步跟进
//1
//XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
//2
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
//3
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
mCallingPackage = new ThreadLocal<>();
if (mContext == null) {
···
ContentProvider.this.onCreate();
}
}

通过上面的分析,可以知道在我们引入依赖后,依赖包中的AndroidMainfest.xml文件便会主动合并到主AndroidManifest.xml文件中,然后在程序启动过程中便会自动创建ContentProvider,然后进行InternalLeakSentry.install(application),接下来进行一些列的监控和dump操作等。

#####2.1 InternalLeakSentry.install(application)

下面来分析InternalLeakSentry.install(application)里面都做了一些什么,可以看到

fun install(application: Application) {
CanaryLog.d(“Installing LeakSentry”)
checkMainThread()
if (this::application.isInitialized) {
return
}
InternalLeakSentry.application = application

val configProvider = { LeakSentry.config }
// 1.监听 Activity.onDestroy()
ActivityDestroyWatcher.install(
application, refWatcher, configProvider
)
// 2.监听 Fragment.onDestroy()
FragmentDestroyWatcher.install(
application, refWatcher, configProvider
)
// 3.监听完成后进行一些初始化工作
listener.onLeakSentryInstalled(application)
}

从命名上可以看到在Activity和Fragment进行destory的时候进行watch

  1. ActivityDestroyWatcher

internal class ActivityDestroyWatcher private constructor(
private val refWatcher: RefWatcher,
private val configProvider: () -> Config
) {

private val lifecycleCallbacks = object : ActivityLifecycleCallbacksAdapter() {
override fun onActivityDestroyed(activity: Activity) {
if (configProvider().watchActivities) {
// 监听到 onDestroy() 之后,通过 refWatcher 监测 Activity
refWatcher.watch(activity)
}
}
}

companion object {
fun install(
application: Application,
refWatcher: RefWatcher,
configProvider: () -> Config
) {
val activityDestroyWatcher =
ActivityDestroyWatcher(refWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
}

  1. FragmentDestroyWatcher

internal interface FragmentDestroyWatcher {

fun watchFragments(activity: Activity)

companion object {

private const val SUPPORT_FRAGMENT_CLASS_NAME = “androidx.fragment.app.Fragment”

fun install(
application: Application,
refWatcher: RefWatcher,
configProvider: () -> LeakSentry.Config
) {
val fragmentDestroyWatchers = mutableListOf()

//大于等于android O
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(refWatcher, configProvider)
)
}

if (classAvailable(
SUPPORT_FRAGMENT_CLASS_NAME
)
) {
// androidx 使用 SupportFragmentDestroyWatcher
fragmentDestroyWatchers.add(
SupportFragmentDestroyWatcher(refWatcher, configProvider)
)
}

if (fragmentDestroyWatchers.size == 0) {
return
}

application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacksAdapter() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
watcher.watchFragments(activity)
}
}
})
}

private fun classAvailable(className: String): Boolean {
return try {
Class.forName(className)
true
} catch (e: ClassNotFoundException) {
false
}
}
}
}

Android O 及以后,androidx 都具备对 Fragment 生命周期的监听功能。为什么不监听Android O之前的呢???(待解决)
在版本为1.5.4之前是不支持Fragment内存泄漏监听的,后面版本才加了进来。

  1. listener.onLeakSentryInstalled(application)

该listener的最终实现类是leakcanary-android-core中的InternalLeakCanary类

override fun onLeakSentryInstalled(application: Application) {
this.application = application

val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
//用于发现可能的内存泄漏之后手动调用 GC 确认是否真的为内存泄露
val gcTrigger = GcTrigger.Default

val configProvider = { LeakCanary.config }

val handlerThread = HandlerThread(HeapDumpTrigger.LEAK_CANARY_THREAD_NAME)
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper)
//用于确认内存泄漏之后进行 heap dump 工作。
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, LeakSentry.refWatcher, gcTrigger, heapDumper, configProvider
)
application.registerVisibilityListener { applicationVisible ->
this.applicationVisible = applicationVisible
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
}
addDynamicShortcut(application)
}

这里有个关于GC回收的知识点,我们可以看看优秀的第三方框架都是怎么写的

interface GcTrigger {
fun runGc()
object Default : GcTrigger {
override fun 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 perform a gc.
Runtime.getRuntime()
.gc()
enqueueReferences()
System.runFinalization()
}
private fun 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 (e: InterruptedException) {
throw AssertionError()
}
}
}
}

可以看到,它使用了Runtime.getRuntime().gc()而不是System.gc(),进入System.gc源码一看

public static void gc() {
boolean shouldRunGC;
synchronized (LOCK) {
shouldRunGC = justRanFinalization;
if (shouldRunGC) {
justRanFinalization = false;
} else {
runGC = true;
}
}
if (shouldRunGC) {
Runtime.getRuntime().gc();
}
}

可以看到System.gc源码的还是最终实现是Runtime.getRuntime().gc();但是需要一系列的判断条件,我们手动调用System.runFinalization()可以使gc方法中的justRanFinalizationw为true,从而保证Runtime.getRuntime().gc()会被执行。

####3.如何判断对象可能泄露:ReferenceQueue含义及作用

在Activity/Fragment销毁后,会进行一系列的对象回收,我们把这些对象分别和引用队列进行关联,当某个对象被回收时,**(弱引用一旦变成弱可达(可达性算法分析),引用就会加到引用队列中,然后再进行回收)**我们对象的引用就会被加入到引用队列中。根据该原理进行一系列的操作,最终判断是否内存泄漏。

#####3.1 引用队列

通常我们将其ReferenceQueue翻译为引用队列,换言之就是存放引用的队列,保存的是Reference对象。其作用在于Reference对象所引用的对象被GC回收时,该Reference对象将会被加入引用队列中(ReferenceQueue)的队列末尾。

ReferenceQueue常用的方法:

public Reference poll():从队列中取出一个元素,队列为空则返回null;

public Reference remove():从队列中出对一个元素,若没有则阻塞至有可出队元素;

public Reference remove(long timeout):从队列中出对一个元素,若没有则阻塞至有可出对元素或阻塞至超过timeout毫秒;

  1. 强引用

  2. 软引用

  3. 弱引用

  4. 虚引用(Phantom Reference)

虚引等同于没有引用,这意味着在任何时候都可能被GC回收,设置虚引用的目的是为了被虚引用关联的对象在被垃圾回收器回收时,能够收到一个系统通知。(被用来跟踪对象被GC回收的活动)虚引用和弱引用的区别在于:虚引用在使用时必须和引用队列(ReferenceQueue)联合使用,其在GC回收期间的活动如下:

ReferenceQueue queue=new ReferenceQueue();

PhantomReference pr=new PhantomReference(object,queue);

也即是GC在回收一个对象时,如果发现该对象具有虚引用,那么在回收之前会首先该对象的虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入虚引用来了解被引用的对象是否被GC回收。

#####3.2 GC Root对象

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#####3.3 内存是否泄漏

知道引用队列的原理后,先大概描述一下如何判断是否泄漏,首先创建三个队列

/**

  • References passed to [watch] that haven’t made it to [retainedReferences] yet.
  • watch() 方法传进来的引用,尚未判定为泄露
    /
    private val watchedReferences = mutableMapOf<String, KeyedWeakReference>()
    /
    *
  • References passed to [watch] that we have determined to be retained longer than they should
  • have been.
  • watch() 方法传进来的引用,已经被判定为泄露
    */
    private val retainedReferences = mutableMapOf<String, KeyedWeakReference>()
    private val queue = ReferenceQueue() // 引用队列,配合弱引用使用

//KeyedWeakReference,对象和引用队列进行弱引用关联,所以这个对象一定会被回收
class KeyedWeakReference(
referent: Any,
val key: String,
val name: String,
val watchUptimeMillis: Long,
referenceQueue: ReferenceQueue
) : WeakReference(
referent, referenceQueue
) {
@Volatile
var retainedUptimeMillis = -1L

companion object {
@Volatile
@JvmStatic var heapDumpUptimeMillis = 0L
}

}

如果一个obj对象,它和队列queue进行弱引用关联,在进行垃圾收集时,发现该对象具有弱引用,会把引用加入到引用队列中,我们如果在该队列中拿到引用,则说明该对象被回收了,如果拿不到,则说明该对象还有强/软引用未释放,那么就说明对象还未回收,发生内存泄漏了,然后dump内存快照,使用第三方库进行引用链分析

这里重点强调一点一个对象可能被多个引用持有,比如强引用,软引用,弱引用,只要这个对象还有强引用/软引用,与这个对象关联的任意引用队列就拿不到引用,引用队列就相当于一个通知,多个引用队列和一个对象关联,对象被回收时,多个队列都会受到通知

#####3.4 watch()

@Synchronized fun watch(
watchedReference: Any,
referenceName: String
) {
if (!isEnabled()) {
return
}
//移除队列中将要被 GC 的引用
removeWeaklyReachableReferences()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference = // 构建当前引用的弱引用对象,并关联引用队列 queue
KeyedWeakReference(watchedReference, key, referenceName, watchUptimeMillis, queue)
if (referenceName != “”) {
CanaryLog.d(
“Watching instance of %s named %s with key %s”, reference.className,
referenceName, key
)
} else {
CanaryLog.d(
“Watching instance of %s with key %s”, reference.className, key
)
}

watchedReferences[key] = reference
checkRetainedExecutor.execute {
//如果引用未被移除,则可能存在内存泄漏
moveToRetained(key)
}
}

removeWeaklyReachableReferences()

private fun 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.
// 弱引用一旦变得弱可达,就会立即入队。这将在 finalization 或者 GC 之前发生。
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference? // 队列 queue 中的对象都是会被 GC 的
if (ref != null) {
val removedRef = watchedReferences.remove(ref.key)
if (removedRef == null) {
retainedReferences.remove(ref.key)

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

image.png

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

image.png

  • Kafka的集群
  • 第一个Kafka程序
  • image.png

afka的生产者

image.png

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

image.png

image.png

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

image.png

  • Kafka实战之削峰填谷

image.png

第一个Kafka程序*

  • [外链图片转存中…(img-AD0VBLIo-1714412485625)]

afka的生产者

[外链图片转存中…(img-MSoaNS7S-1714412485625)]

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

[外链图片转存中…(img-1QJi51Ti-1714412485626)]

[外链图片转存中…(img-Kk86FRM0-1714412485626)]

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-01CUt8fi-1714412485626)]

  • Kafka实战之削峰填谷

[外链图片转存中…(img-RyCvwIp0-1714412485626)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值