Java
虚拟机中使用可达性分析的算法来决定对象是否可以被回收。即通过GCRoot
对象为起始点,向下搜索走过的路径(引用链),如果发现某个对象或者对象组为不可达状态,则将其进行回收。
而内存泄漏
指的就是有些对象(短周期对象)没有用了,但是却被其他有用的类(长周期对象)所引用,从而导致无用对象占据了内存空间,形成内存泄漏。
所以上面的问题,如果仅仅回答内部类持有了外部类的引用
,没有指出内部类被谁所引用,那么按道理来说是不会发生内存泄漏的,因为内部类和外部类都是无用对象了,是可以被正常回收
的。
所以这一题的关键在于,内部类被谁
引用了?也就是Handler被谁引用了?
一起通过实践研究下吧~
Handler发生内存泄漏的情况
1、发送延迟消息
第一种情况,是通过handler
发送延迟消息:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_handler)
btn.setOnClickListener {
//跳转到HandlerActivity
startActivity(Intent(this, HandlerActivity::class.java))
}
}
}
class HandlerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_handler2)
//发送延迟消息
mHandler.sendEmptyMessageDelayed(0, 20000)
btn2.setOnClickListener {
finish()
}
}
val mHandler = object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
btn2.setText(“2222”)
}
}
}
我们在HandlerActivity
中,发送一个延迟20s的消息。然后打开HandlerActivity
后,马上finish。看看会不会内存泄漏。
查看内存泄漏并分析
现在查看内存泄漏还是蛮方便的了,AndroidStudio
自带对堆转储(Heap Dump)文件进行分析,并且会把内存泄漏点明确标出来。
我们运行项目,点击Profiler——Memory
,就能看到以下图片了,一个正在运行的内存情况实时图:
可以看到图片中有两个按钮我标出来了:
-
捕获堆转储文件按钮
,也就是生成hprof文件,这个文件会展示Java堆的使用情况,点击这个按钮后,AndroidStudio会帮我们生成这个堆转储文件并且进行分析。 -
GC按钮
,一般我们在我们捕获堆转储文件之前,点一下GC,就能把一些弱引用给回收,防止给我们分析带来干扰。
所以我们打开HandlerActivity
后,马上finish
,然后点击GC按钮,再点击捕获堆转储文件按钮。AndroidStudio
会自动跳转到以下界面:
可以看到左上角有一个Leaks
,这就是你内存泄漏的点,点击就能看到内存泄漏的类了。右下角就是内存泄漏类的引用路径。
从这张图可以看到,我们的HandlerActivity
发生了内存泄漏,从引用路径来看,是被匿名内部类的实例mHandler
持有引用了,而Handler
的引用是被Message
持有了,Message
引用是被MessageQueue
持有了…
结合我们所学的Handler知识和这次引用路径分析,这次内存泄漏完整的引用链应该是:
主线程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity
所以这次引用的头头
就是主线程
,主线程肯定是不会被回收的,只要是运行中的线程
都不会被JVM回收,跟静态变量
一样被JVM特殊照顾。
这次内存泄漏的原因算是搞清楚了,当然Handler
内存泄漏的情况不光这一种,看看第二种情况:
2、子线程运行没结束
第二个实例,是我们常用到的,在子线程中工作,比如请求网络,然后请求成功后通过Handler
进行UI更新。
class HandlerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_handler2)
//运行中的子线程
thread {
Thread.sleep(20000)
mHandler.sendEmptyMessage(0)
}
btn2.setOnClickListener {
finish()
}
}
val mHandler = object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
btn2.setText(“2222”)
}
}
}
同样运行后看看内存泄漏情况:
可以发现,这里的内存泄漏主要的原因是因为这个运行中的子线程
,由于子线程这个匿名内部类
持有了外部类的引用,而子线程本身是一直在运行的,刚才说过运行中的线程是不会被回收的,所以这里内存泄漏的引用链
应该是:
运行中的子线程 —> Activity
当然,这里的Handler
也是持有了Activity
的引用的,但主要引起内存泄漏的原因还是在于子线程本身,就算子线程中不用Handler
,而是调用Activity
的其他变量或者方法还是会发生内存泄漏。
所以这种情况我觉得不能看作Handler
引起内存泄漏的情况,其根本原因是因为子线程引起的,如果解决了子线程的内存泄漏,比如在Activity
销毁的时候停止子线程,那么Activity
就能正常被回收,那么也不存在Handler
的问题了。
延伸问题1:内部类为什么会持有外部类的引用
这是因为内部类虽然和外部类写在同一个文件中,但是编译后还是会生成不同的class
文件,其中内部类的构造函数中会传入外部类的实例,然后就可以通过this$0
访问外部类的成员。
其实也挺好理解的吧,因为在内部类中可以调用外部类的方法,变量等等,所以肯定会持有外部类的引用的。
贴一段内部类在编译后用JD-GUI
查看的class
代码,也许你能更好的理解:
//原代码
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;
}
}
延伸问题2:kotlin中的内部类与Java有什么不一样吗
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
以上进阶BATJ大厂学习资料可以免费分享给大家,需要完整版的朋友,点这里可以看到全部内容。
-
自行下载直达领取链接:【GitHub】
进阶学习视频
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
可以免费分享给大家,需要完整版的朋友,点这里可以看到全部内容。**
-
自行下载直达领取链接:【GitHub】
进阶学习视频
[外链图片转存中…(img-u6zoG4u1-1711106579734)]
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)