当前位置: 首页 > wzjs >正文

网站开发培训教程网络推广推广培训

网站开发培训教程,网络推广推广培训,策划运营,wordpress导office目录 他是如何检测内存泄漏的?监听每个四大组件的生命周期学习他,你会知道如何设计一个好的框架,无侵入式的。 一、如何实现低侵入性? LeakCanary 不需要手动写代码初始化,只需要在 gradle 中添加依赖就好了&#x…

目录

  1. 他是如何检测内存泄漏的?监听每个四大组件的生命周期
  2. 学习他,你会知道如何设计一个好的框架,无侵入式的。

一、如何实现低侵入性?

LeakCanary 不需要手动写代码初始化,只需要在 gradle 中添加依赖就好了,然后 App 在启动时就会自动运行,年轻的时候我也非常好奇是怎么实现的,其实就是通过 ContentProvider 实现的,它可能是存在感最低的四大组建,但是 ContentProvider 他有一个特点,在 App 初始化的过程中也会初始化 ContentProvider

二、他是如何检测内存泄漏的?

1.1 监听Activity的生命周期

接下来,我们看看Application的registerActivityLifecycleCallbacks方法

在这里插入图片描述

通过 registerActivityLifecycleCallbacks() 方法,Application 可以监听所有 Activity 的生命周期回调。

LeakCanary 在初始化时,通过 Application 注册 ActivityLifecycleCallbacks,从而自动监控所有 Activity 的 onDestroy() 事件。

这也是他的高明之处,无侵入式。LeakCanary 只需在 Application 中注册一次,即可覆盖所有 Activity,无需在每个 Activity 中手动添加代码。


简略代码如下

 // 注册 Activity 生命周期回调application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {override fun onActivityDestroyed(activity: Activity) {// 当 Activity 销毁时,将其交给 watchObject 监控watchObject(activity, "Activity: ${activity.javaClass.simpleName}")}// 其他生命周期方法空实现...})

1.2 如何监控对象,检查是否存在泄漏

在了解是否存在泄漏,我们需要先了解一下什么是引用。

因为在Activity销毁的时候,他需要判断哪些对象可以被回收,哪些不可以。为什么不可以,就跟他引用类型有关。

  1. 强引用(Strong Reference):默认引用类型,通过 new 关键字创建。只要强引用存在,对象​​不会被垃圾回收(GC)​​。当所有强引用断开(如 obj = null),对象才会变为可回收。
Object obj = new Object(); // 强引用
  1. 软引用(Strong Reference):描述有用但非必需的对象,适用于内存敏感缓存。内存充足时,对象保留;​​内存不足时,GC 可能回收​​。
SoftReference<Object> softRef = new SoftReference<>(new Object());
  1. 弱引用:强度低于软引用,对象只能存活到下一次 GC。无论内存是否足够,GC 运行时必回收​​。下一次 GC 触发时回收。
WeakReference<Object> weakRef = new WeakReference<>(new Object());
  1. 虚引用:最弱引用,无法通过 get() 获取对象,仅用于跟踪回收状态。
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);

好的,了解了这些引用知识后,我们就可以知道,如果有哪些在页面销毁后,我们触发GC,但是对象都无法回收,那么就是内存泄漏了。

但我们如何判断对象是否可以被回收呢?这里就需要引入一个新的概念,引用队列。

引用队列(ReferenceQueue)是内存泄漏检测的 ​​事件触发器​​ 和 ​​资源清理器​​,它解决了两个关键问题:

  1. ​精准判断对象是否被回收​​(避免误判/漏判)
  2. ​高效清理无效引用​​(避免内存浪费)

引用队列​​ 是一个用于跟踪对象回收状态的工具,当使用 WeakReferenceSoftReferencePhantomReference 时,若关联的引用队列(ReferenceQueue),​​对象被垃圾回收后,对应的引用(Reference)会被自动加入队列​​。

接下来,我们知道了哪些对象会被回收,那么我们只需要进行对比,不能被回收的activity实例,就存在内存泄漏


// 极简版内存泄漏检测工具(仅监控 Activity)
class MiniLeakCanary private constructor(private val context: Context) {// 引用队列,用于判断对象是否被回收private val referenceQueue = ReferenceQueue<Any>()// 存储被监控对象的弱引用private val watchedObjects = mutableMapOf<String, WeakReference<Any>>()companion object {fun install(application: Application) {MiniLeakCanary(application).watchActivities()}}// 监控所有 Activityprivate fun watchActivities() {(context as MyApp).registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {override fun onActivityDestroyed(activity: Activity) {Log.e("MiniLeakCanary", "onActivityDestroyed")watchObject(activity, "Activity: ${activity.javaClass.simpleName}")}})}// 监控任意对象fun watchObject(obj: Any, tag: String) {val ref = WeakReference(obj, referenceQueue)//为什么叫引用队列呢?里面存储了多少数据?watchedObjects[tag] = ref//只有一个数据,为什么要用map来存储。因为他是监听所有的activity的。checkLeak()}// 检查泄漏private fun checkLeak() {Log.d("checkLeak", "checkLeak: "+watchedObjects.size)// 移除已被回收的引用var ref: Reference<out Any>?while (referenceQueue.poll().also { ref = it } != null) {watchedObjects.entries.removeAll { it.value == ref }}// 延迟 5 秒后再次检查(模拟 LeakCanary 等待 GC)Handler(Looper.getMainLooper()).postDelayed({triggerGcAndCheck()}, 5000)}// 触发 GC 并检查未回收对象private fun triggerGcAndCheck() {// 触发 GC(仅调试用,生产环境不推荐)Runtime.getRuntime().gc()System.runFinalization()// 检查未被回收的对象watchedObjects.forEach { (tag, ref) ->if (ref.get() != null) {Log.e("MiniLeakCanary", "可能内存泄漏: $tag")// 此处可生成 Heap Dump(需复杂实现)}}Log.d("MiniLeakCanary", "triggerGcAndCheck: ")}
}

1.3 Heap Dump的实现

上述,我们只是知道了那个Activity泄漏了,但是不知道具体是那个对象。我们看到Leakcanary里面都有的。所以下面我们来看看Heap Dump的实现。

  • ​Heap Dump​

    • 通过 Debug.dumpH() 生成 .hprof 文件。
    • 文件路径通常存放在应用缓存目录。
  • ​泄漏分析​

    • 解析 .hprof 文件,找到未回收的 KeyedWeakReference
    • 通过引用链分析,定位泄漏路径。

代码实现:我们搞一个有问题的代码


class MainActivity : AppCompatActivity()  {private  val TAG = "MainActivity"private val REQUEST_CODE_LOCATION = 1// 静态变量持有 Activity 实例(导致泄漏的关键)companion object {var leakedActivity: MainActivity? = null}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContentView(R.layout.activity_main)ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)insets}this.findViewById<TextView>(R.id.tv_hello).setOnClickListener {var intent = Intent(this, Main2Activity::class.java)intent.putExtra("name", "Lance");intent.putExtra("boy", "23");startActivity(intent)finish()}// 将当前 Activity 赋值给静态变量leakedActivity = this}
}

增加dumpHeap的实现。

// 触发 GC 并检查未回收对象
private fun triggerGcAndCheck() {// 触发 GC(仅调试用,生产环境不推荐)Runtime.getRuntime().gc()System.runFinalization()// 检查未被回收的对象watchedObjects.forEach { (tag, ref) ->if (ref.get() != null) {Log.e("MiniLeakCanary", "可能内存泄漏: $tag")// 此处可生成 Heap Dump(需复杂实现)val heapDumpFile: File = dumpHeap()!!Log.e("LeakDetector","内存泄漏 detected! Heap dump saved to: $heapDumpFile")}}Log.d("MiniLeakCanary", "triggerGcAndCheck: ")
}// 生成 Heap Dump 文件
private fun dumpHeap(): File? {val heapDumpDir: File = File(myapp.getExternalFilesDir(null), "heap_dumps")if (!heapDumpDir.exists()) {heapDumpDir.mkdirs()}val fileName = "leak_dump_" + System.currentTimeMillis() + ".hprof"val heapDumpFile = File(heapDumpDir, fileName)try {Debug.dumpHprofData(heapDumpFile.absolutePath)return heapDumpFile} catch (e: IOException) {Log.e("LeakDetector", "生成 Heap Dump 失败", e)return null}
}

在这里插入图片描述

双击打开他,就会自动打开android studio的Profile

​(1) 匿名内部类泄漏​
  • ​特征​​:
    类名包含 $1$2(如 MainActivity$1)。
  • ​分析步骤​​:
    查看引用链中是否有 HandlerRunnableThread 持有外部类(如 Activity)的引用。
​(2) 单例/静态变量泄漏​
  • ​特征​​:
    类名包含 ManagerUtilsInstance,或字段被 static 修饰。
  • ​分析步骤​​:
    检查单例对象是否直接或间接持有了 Context/Activity。
​(3) 未反注册监听器​
  • ​特征​​:
    引用链中出现 BroadcastReceiverEventBusOnClickListener 等监听器。
  • ​分析步骤​​:
    检查这些监听器是否在 Activity 销毁时被反注册。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
点击Jump toSource就可以调整到问题的地方。其实原理就是解析了hprof文件,LeakCanary 的堆分析引擎 ​​Shark​​ 是开源的,可直接集成到代码中解析 .hprof


三、为什么要学习他的代码,我们要了解他的开发设计思维

  1. 如何实现无侵入式

  2. 要考虑,写一次代码,其他关联的地方都会增加,而不是每次用到相关的,我都要增加。当然这个适用于有这种全局监听的,也就是有一个上层的,或者你可以增加一个中间层去实现。

四、leakcanary不能完全解决内存泄漏问题

  1. LeakCanary仅监控特定对象​​:默认只检测 ActivityFragment 的泄漏
  2. 内存抖动(Memory Churn)​​:频繁创建/销毁对象导致 GC 压力,LeakCanary 无法直接检测。
  3. LeakCanary官方建议仅在 Debug 构建中使用,无法监控生产环境问题。

LeakCanary 用于日常开发预防,Profiler 用于深度优化和疑难杂症。

  1. Profiler定期手动检查内存使用。
  2. 在出现性能问题时深入分析。
http://www.dtcms.com/wzjs/63458.html

相关文章:

  • 做网站用php如何学习使用网站模板快速建站
  • 兰州企业 网站建设石家庄谷歌seo
  • 网站备案状态查询最近五天的新闻大事
  • 营销型网站建设都具有哪些优势百度站长平台电脑版
  • led网站建设百度网盘客服在线咨询
  • 福田区住房和建设局官方网站千锋教育培训机构就业率
  • 网站地址怎么做超链接爱站关键词
  • 做网站会被捉吗湖南网站seo营销
  • 什么叫高端网站定制河北网站推广公司
  • 网站建设客户分析调查表凡科建站app
  • 虚拟主机网站500错误嘉峪关seo
  • 做护理简历的网站河北seo推广方案
  • 温州有限公司求职seo
  • 杭州建站模板制作软文写作500字
  • 建设黄色网站 什么罪绍兴seo排名收费
  • 如何自己建网站百度官方网站
  • 网站一直做竞价么百度指数的特点
  • 网站建设与管理课程设计开发一个app平台大概需要多少钱?
  • 网站建设 电话咨询关键词优化策略
  • 收费小说网站怎么做网站制作方案
  • 西安注册公司虚拟地址关键词seo如何优化
  • wordpress后台操作视频教程廊坊seo整站优化软件
  • wordpress上长缺少临时文件夹宁波seo优化报价多少
  • 免费网站大全app今日十大新闻
  • 腾讯云注册域名后怎么做网站新出的app推广在哪找
  • 英语翻译网站开发快速整站优化
  • 如何做外贸网站南宁seo服务公司
  • 南通网站搭建定制微友圈推广平台怎么加入
  • 怎么做网站实惠在线培训app
  • 文本文档做网站如何营销推广