Android 内存泄漏 内存溢出 数组越界 ANR

    在日常面试过程中,经常会问到内存泄漏,内存溢出 数组越界 和ANR相关的问题,很多时候可能会分不清内存泄漏和内存溢出以及数组越界的区别,这里就简单地写下自己的看法,如果有什么理解上错误,欢迎大家指出。

1.内存泄漏

内存泄漏 Memory Leak ,对象已经在内存堆栈中分配了空间,但是出于某些原因已经没有使用的价值,但是还是直接或者间接被引用到,导致gc无法回收,如果是一次内存泄漏的危害及影响并不大,并不会导致程序出现闪退现象,而内存泄漏的次数多了就可能会引起内存耗尽 进而诱发内存溢出(OOM), 进而引起程序闪退,为什么说是可能会引起内存耗尽呢?因为java有GC机制,当你的内存紧张时会频繁触发GC,而GC是非常耗时的操作,会导致严重的卡顿体验上会极差,另外,当你的应用处于LRU即缓存列表中,即切换到后台,变为后台进程时,由于内存泄漏而消耗了更多内存,当系统资源不足而需要回收一部分缓存进程时,你的应用很大可能性会被被系统杀死。

内存泄漏常见的原因和如何解决

1.资源未关闭造成的内存泄漏 

BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,避免这些资源将不会被回收,进而造成内存泄漏。

2.谨慎使用static关键字,万不能使用static修饰Activity context

    static关键字修饰的对象占用内存,并且内存一般不会释放,只有内存不够用的时候才会释放静态内存(这里还有个隐患就是 可能会引起访问全局静态错误),如果使用static修饰Activity context会导致activity的所有组件对象都存入全局内存中,并且不会被回收。

3.单例造成的内存泄漏

单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。

4.内部类造成的内存泄漏

尤其是Activity的内部类的生命周期,尽量使用静态内部类代替内部类,如果内部类需要访问外部类的成员,可以用“静态内部类+弱引用”代替;内部类的生命周期不应该超出外部类,外部类结束前,应该及时结束内部类生命周期(停止线程、AsyncTask、TimerTask、Handler消息等,移除类变量或长生命周期的线程对Callback、listener等的强引用)

更多避免内存泄漏要注意的:

1.属性动画在Activity销毁前记得cancel

2.文件流、Cursor等资源用完及时关闭;

3.Activity销毁前WebView的移除和销毁;

4.使用别人的方法(尤其是第三方库),遇到需要传递context时尽量使用ApplicationContext,而不要轻易使用Activity context,因为你不知道别人的代码内部会不会造成该context的泄漏.

常见的分析手段

借助 内存泄漏检测工具LeakCanary 和 内存分析工具——MAT

2.内存溢出 OOM

系统会给每个APP分配内存也就是Heap Size值。当APP占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存时就会抛出的Out Of Memory异常。

内存溢出 OOM常见的原因和如何解决

1.大量内存泄漏

上面的造成内存泄漏原因都有可能导致内存溢出,因为大量内存泄漏会导致内存耗尽从而引发内存溢出,根本解决方案就是解决内存泄露的问题,如何解决如上

2.频繁申请内存而不释放无用内存

有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存,对内存的回收和分配造成很大影响的,容易迫使虚拟机不得不给该应用进程分配更多的内存,造成不必要的内存开支从而诱发内存泄漏,解决方案尽量避免重复申请内存空间以及及时释放无用的内存

3.Bitmap使用不当

众所周知Bitmap的是比较耗内存的,Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null.,在使用过程中尽量采用低内存占用量的编码方式,如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存

4,构造Adapter时,没有复用convertView(即没有使用缓存的 convertView)

如果不复用convertView会导致每次getview()都会重新实例化一个新的View对象,导致内存增加长时间滑动会导致内存耗尽出现OOM ,解决方案复用convertView

常见的分析手段

借助 内存泄漏检测工具LeakCanary 和 内存分析工具 MAT  

3 数组越界

更多的时候事叫 下标越界 和内存溢出的概念很相似,一不小心就会混淆对比来解释下加深记忆

数组越界: 在引用数组元素时,使用的下标超过了该数组下标的应有范围

内存溢出: 在初始化数组(给数组元素赋值)时,初始化元素的个数超过了数组定义时元素的个数。

数组越界是在引用是出现问题 内存溢出是在初始化时出现问题

出现的场景以及解决方案

1.多线程操作同一个数组

多线程操作同一个数组时,同时进行删除和引用可能会导致数组越界,可以在多线程使用是使用同步锁或者使用线程安全的数组对象。

2.引用下标超出数组大小

这种情况就相对简单,可以在判断引用的下标是否大于数组长度-1 ,一般数组的下标是从0开始的 如果大于的话求放弃引用

4.应用程序无响应 ANR

ANR全称:Application Not Responding,也就是应用程序无响应。Android系统中,ActivityManagerService(简称AMS)WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。

ANR的四种类型

InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件

BroadcastQueue Timeout:在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。

Service Timeout:前台服务20秒内,后台服务在200秒内没有执行完毕。

ContentProvider Timeout:ContentProvider的publish在10s内没进行完。

ANR常见的原因和如何解决

1.主线程进行耗时操作(数据库操作,耗时的网络访问等)

在主线程中进行耗时操作会导致主线程卡顿 出现超过5秒无响应 就会出现ANR,应该尽量避免在主线程中进行耗时操作,把耗时操作放到子线程中

2.多线程持死锁导致的ANR

出现这种情况要去具体分析trace文件分析具有哪个线程死锁导致主线block 然后分析向上分析出现问题的原因

3.BroadcastReceiver的onReceive进行耗时操作

在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。如果超时就会出现anr ,如果要执行耗时操作时应启动一个service,将耗时操作交给service来完成,具体问什么不能再onReceive开一个子线程进行处理 详见为什么不能在BroadcastReceiver中开启子线程

4.主线程中加载过大数据和图片

有时候我们需要加载很多高清无码的大图,或者加载特别大的数据时也会出现anr因为这些都是耗内存的操作,但是更新ui的操作又不能放到子线程中,懒汉方法就压缩图片,可以在服务端压缩也可以在客户端压缩,当然Android如果是大数据可以分段加载。

你可能感兴趣的:(Android 内存泄漏 内存溢出 数组越界 ANR)