Android面试集锦系列(18)——内存泥潭(下)

作者 : 开心源码 本文共3457个字,预计阅读时间需要9分钟 发布时间: 2022-05-12 共191人阅读

上一节有详情了少量和内存相关的基础知识,这一节就讲一下怎样发现和解决内存问题。对于我们来说,最容易发现的内存问题当然是OOM(OutOfMemoryError),应用直接Crash,日志也会很清晰的标明哪个对象OOM了。这个处理起来也不难,常见的Bitmap OOM相信大家也知道怎样解决。

相对于OOM较麻烦一点的就是内存泄露(Memory Leak),每次就露那么一点,就像温水煮青蛙一样,很难发现有变化。内存泄漏也是造成应用程序OOM的主要起因之一!我们知道Android系统为每个应用程序分配的内存有限,而当一个应用中产生的内存泄漏比较多时,之后我们再申请新的内存时会及其容易产生OOM。

内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或者间接地引用到GC roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。

面试题:如何检测内存泄露,如何进行内存优化?

Android系统为每一个应用程序都设置了一个硬性的Dalvik Heap Size最大限制阈值,这个阈值在不同的设施上会由于RAM大小不同而各有差异。假如你的应用占用内存空间已经接近这个阈值,此时再尝试分配内存的话,很容易引起OOM。

较简单的查看一个应用的内存使用情况可以通过DDMS的Heap视图查看:

在Android Studio上也可以通过Memory Monitor查看内存中Dalvik Heap的实时变化:

注意:GC过于频繁容易出现内存抖动,这也是造成应用卡慢的常见起因。

也可以通过命行的方式查看:

adb shell dumpsys meminfo <package_name|pid> [-d]

具体的数值意义可以查看官网的说明:https://developer.android.com/studio/profile/investigate-ram.html

MAT内存分析工具

详细的内存使用情况,可以通过Android Studio的Android Monitor界面,在Memory那栏有上几个小图标,点击有一个向下箭头的图标会自动生成并打开的HPROF视图。

不过用他来分析内存泄露还不是很智能,我们可以借助第三方工具,常见的工具就是MAT了(Memory Analyzer Tool),下载地址 http://eclipse.org/mat/downloads.php,这里我们需要下载独立版的MAT(之前在使用Ecelipse开发Android应用时,我们常常会使用它的插件版本)。

注意:Android Monitor生成的HPROF文件为Dalvik虚拟机格式的,需要转成J2SE虚拟机格式的,否则MAT工具中无法打开。转换的方式也很简单,Android Studio自带了,直接在“Captures”->”Heap Snapshot”选中刚刚生成的”.hprof”文件,而后鼠标右键选择“Export to standard .hprof”可以在MAT上使用了。

MAT的具体使用方式,网上很多,大家可以自己搜一下。这里就提一下用它怎样能快速查找到内存泄露的点,比方通过“Dominator Tree”的”Path To GC Roots”的排除虚引用/弱引用/软引用等的引用链,由于被虚引用/弱引用/软引用的对象可以直接被GC给回收,我们要看的就是某个我们已经不需要使用的对象否还存在强引用链。比方,我们已退出一个Activity(onDestroy方法也被执行了),但在Path To GC Roots中却发现这个Activity对象还被有一个引用链,那么即可以确认这个Activity对像就产生了内存泄漏。一般来说,从它的引用链上也可以直观地看出是谁在引用它。

除了上面详情了MAT检测内存泄露, 有一个叫LeakCanary工具大家也可以尝试一下。项目地址: square/leakcanaryLeakCanary会检测应用的内存回收情况,假如发现有垃圾对象没有被回收,就会去分析当前的内存快照,也就是上边MAT用到的.hprof文件,找到对象的引用链,并显示在页面上。这款插件的好处就是,可以在移动端直接查看内存泄露的地方,可以辅助我们检测内存泄露。

开发中如何避免内存泄漏

这点我比较喜欢问面试者,希望面试者能罗列出少量他自己遇到过的情况。通常来说,Activity的泄漏是内存泄漏里面最严重的问题,它占用的内存多(它里面有N多资源的引用),影响比较显著。下面就示例两种错误的引用方式。

错误的单例模式

public class Singleton {    private static Singleton instance;    private Context mContext;    private Singleton(Context context) {        this.mContext = context;    }    public static Singleton getInstance(Context context) {        if (instance == null) {            instance = new Singleton(context);        }        return instance;    }}

这是一个非线程安全的单例模式,instance作为静态对象,其生命周期要长于普通的对象,其中也包含Activity,如果Activity A去getInstance取得instance对象,传入this,常驻内存的Singleton保存了你传入的Activity A对象,并一直持有,即便Activity被销毁掉,但由于它的引用还存在于一个Singleton中,就不可能被GC掉,这样就导致了内存泄漏。

View持有Activity引用

public class MainActivity extends Activity {    private static Drawable mDrawable;    @Override    protected void onCreate(Bundle saveInstanceState) {        super.onCreate(saveInstanceState);        setContentView(R.layout.activity_main);        ImageView iv = new ImageView(this);        mDrawable = getResources().getDrawable(R.drawable.ic_launcher);        iv.setImageDrawable(mDrawable);    }}

有一个静态的Drawable对象当ImageView设置这个Drawable时,ImageView保存了mDrawable的引用,而ImageView传入的this是MainActivity的mContext,由于被static修饰的mDrawable是常驻内存的,MainActivity是它的间接引用,MainActivity被销毁时,也不能被GC掉,所以造成内存泄漏。

其实避免Activity的泄漏的方式可以总结为:不要让生命周期长于Activity的对象持有到Activity的引用。

在开发中,我们也可以给少量初级的工程师相关的建议,如:

  1. 注意单例模式和静态变量能否会持有对Context的引用;
  1. 注意监听器的注销;(在Android程序里面存在很多需要register与unregister的监听器,我们需要确保在合适的时候及时unregister那些监听器。)
  2. 不要在Thread或者AsyncTask中的引用Activity;

小结

内存泄漏检测并不属于一个经常会做的事情,所以上面写的少量东西难免会有少量错误。不过我认为在面试中,更关注的是面试者做何去发现和处理这个问题,而后能否会对遇到过的问题有一个总结,至于细节上的东西在真正做的时候会直接得到工具或者LOG的反馈,不肯定非常记得很清楚的人才说明他会这个东西。

最后

在这里我总结出了互联网公司Android程序员面试简历模板,面试涉及到的绝大部分面试题及答案做成了文档和架构视频资料免费分享给大家【包括高级UI、性能优化、架构师课程、NDK、Kotlin、混合式开发(ReactNative+Weex)、Flutter等架构技术资料】,希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

资料获取方式:加入Android架构交流QQ群聊:513088520 ,进群即领取资料!!!

点击链接加入群聊【Android移动架构总群】:加入群聊

资料大全

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android面试集锦系列(18)——内存泥潭(下)

发表回复