【Android高阶】内存泄露在Android Studio中的检测方式

Android Studio提供了查看和分析内存泄露的方式,具体从哪个版本开始支持我们不做考究:

编写测试代码,一定会内存泄露的:

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

set.setOnClickListener {
 startActivity(Intent(this, HandlerActivity::class.java))
    }
 }
}


class HandlerActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_handler)

mHandler.sendEmptyMessageDelayed(0, 20000)

btn2.setOnClickListener {
 finish()
    }
}

var mHandler = object: Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
btn2?.text = "222"
 }
}
}

一个MainActivity打开HandlerActivity,然后迅速点击按钮关闭。通过如下步骤可以查看内存泄露情况:

 

可以看到我的测试用例里,4个泄露点,右下角References会指出引用关系,我们的HandlerActivity发生了内存泄漏,从引用路径来看,是被匿名内部类的实例mHandler持有引用了,而Handler的引用是被Message持有了,Message引用是被MessageQueue持有了...

结合我们所学的Handler知识和这次引用路径分析,这次内存泄漏完整的引用链应该是:

主线程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity

所以这次引用的头头就是主线程,主线程肯定是不会被回收的,只要是运行中的线程都不会被JVM回收,跟静态变量一样被JVM特殊照顾。

 

问题来了:

一:为什么Activity的引用会被Handler持有? 引申出另一个问题, 非静态内部类为什么持有外部类的引用:

这是因为内部类虽然和外部类写在同一个文件中,但是编译后还是会生成不同的class文件,其中内部类的构造函数中会传入外部类的实例,然后就可以通过this$0访问外部类的成员。

//原代码
class InnerClassOutClass{

    class InnerUser {
       private int age = 20;
    }
}

//class代码
class InnerClassOutClass$InnerUser {
    private int age;
    InnerClassOutClass$InnerUser(InnerClassOutClass var1) {
        this.this$0 = var1;
        this.age = 20;
     }
}

二:kotlin中的内部类与Java有什么不一样吗?

可以看到我在测试代码中加了一句:

btn2?.setText("2222")

如果删掉这句话,你会发现Leaks是0,即不会有内存泄露的发生。

这是因为在kotlin中的匿名内部类分为两种情况:

  • 在Kotlin中匿名内部类如果没有使用到外部类的对象引用时候,是不会持有外部类的对象引用的,此时的匿名内部类其实就是个静态匿名内部类,也就不会发生内存泄漏。

  • 在Kotlin中,匿名内部类如果使用了对外部类的引用,像我刚才使用了btn2,这时候就会持有外部类的引用了,就会需要考虑内存泄漏的问题。

同样kotlin中对于内部类也是和Java有区别的:

  • Kotlin中所有的内部类都是默认静态的,也就都是静态内部类

  • 如果需要调用外部的对象方法,就需要用inner修饰,改成和Java一样的内部类,并且会持有外部类的引用,需要考虑内存泄漏问题。

 

如何解决内存泄露呢?

  • 不要让长生命周期对象持有短生命周期对象的引用,而是用长生命周期对象持有长生命周期对象的引用。

  • 将对象的强引用改成弱引用

强引用就是对象被强引用后,无论如何都不会被回收。
弱引用就是在垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收。
软引用就是在系统将发生内存溢出的时候,回进行回收。
虚引用是对象完全不会对其生存时间构成影响,也无法通过虚引用来获取对象实例,用的比较少。

class HandlerActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_handler)

        MyHandler(WeakReference(this)).sendEmptyMessageDelayed(0, 20000)
    
        btn2.setOnClickListener {
            finish()
        }
    }

    class MyHandler(var mActivity: WeakReference<HandlerActivity>) : Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            mActivity?.get()?.btn2?.text = "222"
        }
    }
}

这样就不会泄露了。

 

参考:https://mp.weixin.qq.com/s?__biz=MzU0MTYwMTIzMw==&mid=2247484982&idx=1&sn=0e3f36dedcf052e8c444f3b8e2bf4437&chksm=fb263a88cc51b39ed588284be6c3975e059c8003dc0aec0bdbc067bb83728ca9186eb329c167&mpshare=1&scene=1&srcid=0208yNfQEabPxpDueQ4sjBov&sharer_sharetime=1612745388588&sharer_shareid=3f6b7d3b6da6f40e3a23b0d4e48030cc&exportkey=AZdLGzq3Uq96srJb0N5xmjs%3D&pass_ticket=vPRx1j44cfdpUHgSei3z3URMcgyvHDnSeetHe4oQJf%2FB%2F9LW89h4wEuHlr%2FlS6RG&wx_header=0#rd

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值