字节跳动、快手等大厂Android面试刨根问底之内存泄露篇

169 阅读8分钟

现在快手字节跳动等公司都在大量招人,薪资优厚,但是想进去却没那么简单,面过的人都知道,这些公司的面试官巴不得把你会的东西都给你挖出来,所以要深入复习知识点,让自己耐问一点。一下是针对内存泄露真实面试过程:

1.什么是内存泄漏

内存泄漏是指程序申请内存之后,无法释放已经申请的内存,也就是该回收的对象不能被回收。一般是持有引用的对象生命周期比该引用生命周期长导致。

2.哪些场景导致内存泄漏

资源对象没有关闭导致内存泄漏,比如广播没有反注册,EventBus没有反注册,文件流没有关闭,数据库指针没有关闭,bitmap用完之后没有回收。

static修饰的成员变量或者单例模式,比如static修饰的context,由于被修饰的对象生命周期和app一样长,可能会导致activity无法被回收。

在非静态内部类中持有静态实例。因为非静态累默认持有外部类的实例,当持有静态实例的时候会导致内部类生命周期和app一样长,从而导致外部类的生命周期也和app一样长而无法被回收。只要把非静态内部类改成静态内部类即可。

3.内存溢出会有什么影响

一次两次内存泄漏出不会造成什么影响,但是内存泄漏多了会导致系统不断发生gc从而导致程序卡顿,同时内存泄漏多了可能会导致系统分配的内存不够用而发生内存溢出crah。

4.如何检测内存泄漏

mat分析内存 集成leakcanery检测内存泄漏 androidstudio lint静态代码检测内存泄漏

5.请描述mat分析内存的过程

在程序运行的时候再as上打开profiler,然后选择memory,在手机上操作,比如关闭对应的activity,然后点击as上的GC,点击dump,一般来说as就可以自动分析hprof文件,整理出对应的内存信息,可以选择按包排序,然后选择我们的程序,看看那些对象还在使用内存,如果应该被回收的对象却还在使用内存,说明已经发生内存泄露了。

然后如果不想在AS上分析,也可以导出hprof文件,去eclipse中找到mat进行分析,但是mat不能识别hprof文件需要使用hprof-conv命令进行转换,命令是hprof-conv 源文件 输出文件,之后就可以使用mat进行内存分析了。

其中最主要的两个工具是Histogran可以看到不同类型buffer数量和占用内存大小,Dominator Tree把对象按照内存大小进行排序,然后我们可以通过merge shortest paths to gc roots,然后选择引用关系,就可以看到究竟是哪内存泄露了。

6.leakecanery的原理了解吗

如果我们想监听一个对象是否被回收,可以采用weakreference和referencequeue的结合的方式进行。我们在构造weakreference的时候传入一个referencequeue实例,当weakreference引用的对象被回收的时候,他会被存在pending内,然后会有个线程无线循环读取这个pending队列,这个weakreference会被添加到这个referencequeue中,这时候我们可以查找referencequeue队列,如果里面有这个weakreference,则说明对应的对象被回收了。

在leakcanery初始化的时候会注册app的lifecycleCallbacks,当activity执行ondestroy的时候leakcanery会收到回调,并拿到对应的activity的引用,此时会调RefWatcher的watcher方法,在这个方法里会通过AndroidWatcherExcetor启动一个Runnable任务,在这个任务中会用构建keyedweakreference,并传入activity对象和referencequeue的实例,并且这个任务会延迟5秒之后再执行。

当activity被回收时,keyedweakreference会被加入referencequeue中,如果发生内存泄漏,则referencequeue无法找到keyedweakreference对象,此时可以判断发生了内存泄漏。所以在这个任务中,会调用ensureGone方法去referencequeue查找是否有keyedweakreference对象,如果有则说明发生了内存泄露,activity没有被回收。则会再次调用GC方法再回收一次,如果此时referencequeue中仍fan没有keyedweakreference的对象,则说明肯定存在内存泄露,则通过heapDumper分析prof文件,并通过heampdumplister回调结果到通知栏。

7.leakecanery具体是如何判断有没有发生内存泄露的呢?

刚才说过,当WeakReference里传入ReferenceQueue的时候,如果WeakReference内持有的对象引用被回收,则会把WeakReference对象添加到ReferenceQueue队列中。利用这个原理,leakcanary在监听到activity执行onDestroy的时候会构建一个keyedWeakReference实例,这个keyedWeakReference继承自weakReference,里面包含key和name字段

构建keyedWeakReference实例的时候会随机生成一个key,传进去,也就是这个弱引用对象和这个key是绑定的。同时,会把这个key存入到Set retainedKeys中。当执行ensureGone方法的时候会先去keyedWeakReference队里中查找WeakReference对象,如果存在,则说明已经回收成功,则从retainedKeys中移除对应的key,然后再检验retainedKeys中是否还存有该WeakReference.key,如果不存在了,则说明已经成功回收,如果还存在,则说明没有成功回收,此时出发一次GC,再走一遍刚才的流程,如果仍然存在key,则说明确定发生内存泄露了。上面讲到,检测内存泄露的任务延迟了5秒钟,已经给足了回收的时间,如果此时都不能被回收掉,说明activity肯定发生了内存泄露。

8.检测内存泄露的任务发生在哪个线程?

通过Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler()) 等待主线程空闲的时候发送到子线程中,并延迟5秒。

9.什么叫内存溢出

内存溢出就是程序在申请内存的时候系统可用内存或者系统分配的内存不够了导致发生oom异常。如果内存溢出得多了就会导致内存溢出。

10.内存溢出一般发生在就JVM哪个区

JVM中程序技计数器,虚拟机栈,本地方法栈都是随线程生,随线程灭,帧栈随方法进入和退出做了入栈和出栈处理,实现了内存自动清理,所以内存溢出一般会发生在堆区和方法区,其中堆区用于堆区用于存放对象的实例,是垃圾收集器的主要管理区域,并且分代管理对象;方法区一般存放虚拟机加载的类信息,常量,静态变量,编译后的代码和数据,GC主要回收方法区的常量和类卸载。这两个区域都可能发生内存溢出.

这些都是常见的面试题,我整理出来分享给大家。此外我还把Github上8.3k星的Android性能优化教程和实战项目整合成了一个PDF文档。

资料全部整理下来有722页,涵盖设计思想与代码质量优化、程序性能优化、开发效率优化、Github高星优化实战项目解析,其中详细讲解了启动优化、布局优化、内存优化、屏幕适配、OOM问题等方面。有兴趣的朋友【关注+点赞+简信】免费获取。

Github高星优化实战项目解析

1.启动速度 2.流畅度 3.抖音在APK包大小资源优化的实践 4.优酷响应式布局技术全解析 5.网络优化 6.手机淘宝双十一性能优化项目揭秘 7.高德APP全链路源码依赖分析 8.彻底干掉OOM的实战经验分享 9.微信Android终端内存优化实践

设计思想与代码质量优化

1.六大原则. 2.设计模式 3.数据结构 4.算法

程序性能优化

1.启动速度与执行效率优化 2.布局检测与优化 3.内存优化 4.耗电优化 5.网络传输与数据储存优化 6.APK大小优化 7.屏幕适配 8.OOM问题原理解析 9.ANR问题解析 10.Crash监控方案

开发效率优化

1.分布式版本控制系统Git 2.自动化构建系统Gradle

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。以上知识笔记全部免费分享,如有需要获取知识笔记的朋友,【点击我】阅读下载。**

另外附上一份收集的各大厂面试题(附答案),需要的朋友【点击我】阅读下载。