Android 内存优化OOM 秒变大神 内存泄漏_ 性能优化(四)
来源:互联网 发布:p2p网络运营招聘 编辑:程序博客网 时间:2024/06/06 01:17
Android 性能优化 (一)APK高效瘦身
http://blog.csdn.net/whb20081815/article/details/70140063
Android 性能优化(三)布局优化 秒变大神
http://blog.csdn.net/whb20081815/article/details/70147958
Android 性能优化<七>自定义view绘制优化
内存泄露的危害:
1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)
2.直接崩溃OOM:(OutOfMemoryError)
3.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反之内存占用越小,在后台存在的时间就越长)
程序内存的大小:
Android为每个进程设置Dalvik Heap Size阈值,这个阈值在不同的设备上会因为RAM大小不同而各有差异。如果APP想要分配的内存超过这个阈值,就会发生OOM。程序的可用内存有多大.有限的堆内存,原始只有16M
Java虚拟机对内存的管理(分配和回收)原理:
Android虚拟机的垃圾回收采用的是根搜索算法,还一种是程序计数器算法。GC会从根节点(GC Roots)开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。而内存泄漏出现的原因就是存在了无效的引用,导致本来需要被GC的对象没有被回收掉。
垃圾回收算法有很多种,这里介绍Java中常见的垃圾回收算法:
垃圾回收器(GC)把栈上的一些引用所关联的对象作为根节点(GC Root),根据这些引用去搜索与其关联的对象,搜索所经过的节点所组成的路径称为GC链。比如有三个类A,B,C,其中,A持有B的应用,B持有C的引用,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
当执行:
- 1
- 2
- 3
- 1
- 2
- 3
我们就可以通过引用a来找到C的对象,这一条链就可以作为GC链。
当一个对象从GC Root有路径可达,就说明这个对象正在被引用。GC对于这种对象会“网开一面”。如果有对象没有任何GC Root可达,GC就会对这些对象打上标记,方便后面回收。
1)共享内存
Android系统通过下面几种方式来实现共享内存:
Android应用的进程都是从一个叫做Zygote的进程fork出来的。Zygote进程在系统启动,并载入通用的framework的代码与资源之后开始启动。为了启动一个新的程序进程,系统会fork Zygote进程生成一个新的进程,然后在新的进程中加载并运行应用程序的代码。这就使得大多数的RAM pages被用来分配给framework的代码,同时促使RAM资源能够在应用的所有进程之间进行共享。
大多数static的数据被mmapped到一个进程中。这不仅仅让同样的数据能够在进程间进行共享,而且使得它能够在需要的时候被paged out。常见的static数据包括Dalvik Code、app resources、so文件等。
大多数情况下,Android通过显式的分配共享内存区域(例如ashmem或gralloc)来实现动态RAM区域能够在不同进程之间进行共享的机制。比如,Window Surface在App与Screen Compositor之间使用共享的内存,Cursor Buffers在Content Provider与Clients之间共享内存。
2)分配与回收内存
每一个进程的Dalvik Heap都反映了使用内存的占用范围。这就是通常逻辑意义上提到的Dalvik Heap Size,它可以随着需要进行增长,但是增长行为会有一个系统为它设定上限。
逻辑上讲的Heap Size和实际物理意义上使用的内存大小是不对等的,Proportional Set Size(PSS)记录了应用程序自身占用以及与其他进程进行共享的内存。
Android系统并不会对Heap中空闲内存区域做碎片整理。系统仅仅会在新的内存分配之前判断Heap的尾端剩余空间是否足够,如果空间不够会触发GC操作,从而腾出更多空闲的内存空间。在Android的高级系统版本里面针对Heap空间有一个Generational Heap Memory的模型,最近分配的对象会存放在Young Generation区域。当这个对象在该区域停留的时间达到一定程度,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域。系统会根据内存中不同的内存数据类型分别执行不同的GC操作。例如,刚分配到Young Generation区域的对象通常更容易被销毁回收,同时在Young Generation区域的GC操作速度会比Old Generation区域的GC操作速度更快(如图1所示)。
内存优化方案:5R:
本文主要通过如下的5R方法来对ANDROID内存进行优化:
1.Reckon(计算)
首先需要知道你的app所消耗内存的情况,知己知彼才能百战不殆
2.Reduce(减少)
消耗更少的资源
3.Reuse(重用)
当第一次使用完以后,尽量给其他的使用
4.Recycle(回收)
返回资源给生产流
5.Review(检查)
回顾检查你的程序,看看设计或代码有什么不合理的地方。
一:内存的计算:Reckon
二. 内存的减少Reduce
1)使用更加轻量的数据结构
2)避免在Android里面使用Enum
Android官方培训课程提到过“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.”
3)减小Bitmap对象的内存占用
Bitmap是一个极容易消耗内存的大胖子,减小创建出来的Bitmap的内存占用可谓是重中之重,通常来说有以下2个措施:
inSampleSize:缩放比例,在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。
decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。
4).Services资源,一个Services节省4M
5)StringBuilder
在有些时候,代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用StringBuilder来替代频繁的“+”。
三.Reuse(重用)
1).ListView/GridView等出现大量重复子组件的视图里对ConvertView的复用
2).Bitmap对象的复用
在ListView与GridView等显示大量图片的控件里,需要使用LRU的机制来缓存处理好的Bitmap
3).池(PooL),线程池
4).避免在onDraw方法里面执行对象的创建
5.类似onDraw等频繁调用的方法,一定需要注意避免在这里做创建对象的操作,因为他会迅速增加内存的使用,而且很容易引起频繁的gc,甚至是内存抖动。
四.Recycle(回收)
内存的回收
http://blog.csdn.net/hewence1/article/details/39233085
修改对象引用类型:
1).注意临时Bitmap对象的及时回收
2).引用类型:
引用分为四种级别,这四种级别由高到低依次为:强引用>软引用>弱引用>虚引用。
强引用(strong reference)
如:Object object=new Object(),object就是一个强引用了。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
软引用(SoftReference)
只有内存不够时才回收,常用于缓存;当内存达到一个阀值,GC就会去回收它;
弱引用(WeakReference)
弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference)
"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
软引用和弱引用的应用实例:
注意:对于SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,现在已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,所以下面的内容可以选择忽略。
在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。
五.Review(检查)
onLowMemory()与onTrimMemory()
内存泄漏的常见的情况:
http://blog.csdn.net/u010198148/article/details/51649852
http://www.aichengxu.com/java/17381.htm
http://www.cnblogs.com/qianxudetianxia/p/3645106.html
什么是内存泄漏
定义了的变量没使用,就是内存泄漏了。
Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。
据统计,94%得OOM异常都是由于内存泄露引发的。所以,解决内存泄露是我们Android程序员必须面对的话题。
要解决MainActivity的内存泄漏问题,只需把非静态的Thread匿名类定义成静态的内部类就行了(静态的内部类不会持有外部类的一个隐式引用):
内存泄漏1:静态Activities(static Activities)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
LeakCanary检
静态变量在整个应用的内存里只保存一份,一旦创建就不会释放该变量的内存,直到整个应用都销毁才会释放static静态变量的内存.
为什么?
在上面代码中,我们声明了一个静态的Activity变量并且在TextView的OnClick事件里引用了当前正在运行的Activity实例,所以如果在activity的生命周期结束之前没有清除这个引用,则会引起内存泄漏。因为声明的activity是静态的,会常驻内存,如果该对象不清除,则垃圾回收器无法回收变量。
怎么解决?
最简单的方法是在onDestory方法中将静态变量activity置空,这样垃圾回收器就可以将静态变量回收。
- Activity Context被传递到其他实例中,这可能导致自身被引用而发生泄漏。
- 解决:对于大部分非必须使用Activity Context的情况(创建Dialog的Context必须是Activity Context),应该使用Application Context。
内存泄漏2:静态View
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
LeakCanary检测到的内存泄漏
为什么?
上面代码看似没有问题,在Activity里声明一个静态变量view,然后初始化,当Activity生命周期结束了内存也释放了,但是LeakCanary却显示出现了内存泄漏,为什么?问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。
怎么解决?
在onDestroy方法里将静态变量置空。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
内存泄漏3:非静态内部类内存泄露
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
使用LeakCanary检测到的内存泄漏:
为什么?
非静态内部类会持有外部类的引用,在上面代码中内部类持有Activity的引用,因此inner会一直持有Activity,如果Activity生命周期结束没有清除这个引用,这样就发生了内存泄漏。
怎么解决?
因为非静态内部类隐式持有外部类的强引用,所以我们将内部类声明成静态的就可以了。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
内存泄漏4:匿名内部类内存泄漏
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
使用LeakCanary检测到的内存泄漏:
为什么?
上面代码在activity中创建了一个匿名类AsyncTask,匿名类和非静态内部类相同,会持有外部类对象,这里也就是activity,因此如果你在Activity里声明且实例化一个匿名的AsyncTask对象,则可能会发生内存泄漏,如果这个线程在Activity销毁后还一直在后台执行,那这个线程会继续持有这个Activity的引用从而不会被GC回收,直到线程执行完成。
怎么解决?
自定义静态AsyncTask类,并且让AsyncTask的周期和Activity周期保持一致,也就是在Activity生命周期结束时要将AsyncTask cancel掉。
内存泄漏5:Handler
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
为什么?
当Android Application启动以后,framework会首先帮助我们完成UI线程的消息循环,也就是在UI线程中,Loop、MessageQueue、Message等等这些实例已经由framework帮我们实现了。所有的Application主要事件,比如Activity的生命周期方法、Button的点击事件都包含在这个Message里面,这些Message都会加入到MessageQueue中去,所以,UI线程的消息循环贯穿于整个Application生命周期,所以当你在UI线程中生成Handler的实例,就会持有Loop以及MessageQueue的引用。并且在Java中非静态内部类和匿名内持有外部类的引用,而静态内部类则不会持有外部类的引用。
- 在Java中,非静态(匿名)内部类会引用外部类对象。而静态内部类不会引用外部类对象。
- 当Activity Finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity(从以上原因可知道)。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
怎么解决?
可以由上面的结论看出,产生泄漏的根源在于匿名类持有Activity的引用,
1.因此可以自定义Handler和Runnable类并声明成静态的内部类,来解除和Activity的引用。
2.把消息对象从消息队列移除就行了。
3.用弱引用
内存泄漏6:Thread
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
为什么?
同AsyncTask一样,这里就不过多赘述。
怎么解决?
和Handler的处理是一样的
Virtual Machine (DVM)会为所有活跃的threads在运行时系统中保持一个硬引用,这会导致threads一直处于运行状态,垃圾收集器将永远不可能回收它.
内存泄漏7:Timer Tasks
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
为什么?
这里内存泄漏在于Timer和TimerTask没有进行Cancel,从而导致Timer和TimerTask一直引用外部类Activity。
怎么解决?
在适当的时机进行Cancel。
内存泄漏8. 监听注册和反注册:evnbus和事件的监听,广播的监听
.BroadcastReceiver对象
原因:没有取消注册。
直接:getContext().unregisterReceiver(receiver);
即可.
注册与反注册:
内存泄漏9:数据库Cursor没有关闭
总结:解决内存泄漏最好的办法:程序的结构MVC分层做好了 就会有比较少的这些handler和thread的内存泄露
可以把要做的业务逻辑和UI变化都放到 一个任务栈里 ,启一个service来遍历这个栈,这样逻辑上清晰内存泄露的杂事也少些1、使用静态内部类/匿名类,不要使用非静态内部类/匿名类.非静态内部类/匿名类会隐式的持有外部类的引用,外部类就有可能发生泄漏。而静态内部类/匿名类不会隐式的持有外部类引用,外部类会以正常的方式回收,如果你想在静态内部类/匿名类中使用外部类的属性或方法时,可以显示的持有一个弱引用。2、不要以为Java永远会帮你清理回收正在运行的threads.在上面的代码中,我们很容易误以为当Activity结束销毁时会帮我们把正在运行的thread也结束回收掉,但事情永远不是这样的!Java threads会一直存在,只有当线程运行完成或被杀死掉,线程才会被回收。所以我们应该养成为thread设置退出逻辑条件的习惯。Dialog对象:使用isFinishing()判断Activity是否退出。才可以showDialog
- Bitmap没调用recycle()
- 资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们
- 构造Adapter时,没有使用缓存的 convertView
- 集合容器对象没清理造成的内存泄露(尤其是static的集合,需要clear清理)
- WebView对象没有销毁。它的destory()函数来销毁它
解决内存泄漏的工具LeakCanary:
在本篇中,楼主用LeakCanary来对内存泄漏进行检测。LeakCanary是非常好用的第三方库用来进行内存泄漏检测,感兴趣的朋友可以去查阅LeakCanary使用方法,使用它来监测App中的内存泄漏。
leakcanary 原理:http://www.jianshu.com/p/5ee6b471970e
Android的内存优化涉及的知识面还有很多:内存管理的细节,垃圾回收的工作原理,如何查找内存泄漏等等都可以展开讲很多。OOM是内存优化当中比较突出的一点,尽量减少OOM的概率对内存优化有着很大的意义。
http://blog.csdn.net/hewence1/article/details/39004301
http://blog.csdn.net/hewence1/article/details/39233085
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0920/3478.html
内存泄露的危害:
1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)
2.直接崩溃OOM:(OutOfMemoryError)
3.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反之内存占用越小,在后台存在的时间就越长)
程序内存的大小:
Android为每个进程设置Dalvik Heap Size阈值,这个阈值在不同的设备上会因为RAM大小不同而各有差异。如果APP想要分配的内存超过这个阈值,就会发生OOM。程序的可用内存有多大.有限的堆内存,原始只有16M
Java虚拟机对内存的管理(分配和回收)原理:
Android虚拟机的垃圾回收采用的是根搜索算法,还一种是程序计数器算法。GC会从根节点(GC Roots)开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。而内存泄漏出现的原因就是存在了无效的引用,导致本来需要被GC的对象没有被回收掉。
垃圾回收算法有很多种,这里介绍Java中常见的垃圾回收算法:
垃圾回收器(GC)把栈上的一些引用所关联的对象作为根节点(GC Root),根据这些引用去搜索与其关联的对象,搜索所经过的节点所组成的路径称为GC链。比如有三个类A,B,C,其中,A持有B的应用,B持有C的引用,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
当执行:
- 1
- 2
- 3
- 1
- 2
- 3
我们就可以通过引用a来找到C的对象,这一条链就可以作为GC链。
当一个对象从GC Root有路径可达,就说明这个对象正在被引用。GC对于这种对象会“网开一面”。如果有对象没有任何GC Root可达,GC就会对这些对象打上标记,方便后面回收。
1)共享内存
Android系统通过下面几种方式来实现共享内存:
Android应用的进程都是从一个叫做Zygote的进程fork出来的。Zygote进程在系统启动,并载入通用的framework的代码与资源之后开始启动。为了启动一个新的程序进程,系统会fork Zygote进程生成一个新的进程,然后在新的进程中加载并运行应用程序的代码。这就使得大多数的RAM pages被用来分配给framework的代码,同时促使RAM资源能够在应用的所有进程之间进行共享。
大多数static的数据被mmapped到一个进程中。这不仅仅让同样的数据能够在进程间进行共享,而且使得它能够在需要的时候被paged out。常见的static数据包括Dalvik Code、app resources、so文件等。
大多数情况下,Android通过显式的分配共享内存区域(例如ashmem或gralloc)来实现动态RAM区域能够在不同进程之间进行共享的机制。比如,Window Surface在App与Screen Compositor之间使用共享的内存,Cursor Buffers在Content Provider与Clients之间共享内存。
2)分配与回收内存
每一个进程的Dalvik Heap都反映了使用内存的占用范围。这就是通常逻辑意义上提到的Dalvik Heap Size,它可以随着需要进行增长,但是增长行为会有一个系统为它设定上限。
逻辑上讲的Heap Size和实际物理意义上使用的内存大小是不对等的,Proportional Set Size(PSS)记录了应用程序自身占用以及与其他进程进行共享的内存。
Android系统并不会对Heap中空闲内存区域做碎片整理。系统仅仅会在新的内存分配之前判断Heap的尾端剩余空间是否足够,如果空间不够会触发GC操作,从而腾出更多空闲的内存空间。在Android的高级系统版本里面针对Heap空间有一个Generational Heap Memory的模型,最近分配的对象会存放在Young Generation区域。当这个对象在该区域停留的时间达到一定程度,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域。系统会根据内存中不同的内存数据类型分别执行不同的GC操作。例如,刚分配到Young Generation区域的对象通常更容易被销毁回收,同时在Young Generation区域的GC操作速度会比Old Generation区域的GC操作速度更快(如图1所示)。
内存优化方案:5R:
本文主要通过如下的5R方法来对ANDROID内存进行优化:
1.Reckon(计算)
首先需要知道你的app所消耗内存的情况,知己知彼才能百战不殆
2.Reduce(减少)
消耗更少的资源
3.Reuse(重用)
当第一次使用完以后,尽量给其他的使用
4.Recycle(回收)
返回资源给生产流
5.Review(检查)
回顾检查你的程序,看看设计或代码有什么不合理的地方。
一:内存的计算:Reckon
二. 内存的减少Reduce
1)使用更加轻量的数据结构
2)避免在Android里面使用Enum
Android官方培训课程提到过“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.”
3)减小Bitmap对象的内存占用
Bitmap是一个极容易消耗内存的大胖子,减小创建出来的Bitmap的内存占用可谓是重中之重,通常来说有以下2个措施:
inSampleSize:缩放比例,在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。
decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。
4).Services资源,一个Services节省4M
5)StringBuilder
在有些时候,代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用StringBuilder来替代频繁的“+”。
三.Reuse(重用)
1).ListView/GridView等出现大量重复子组件的视图里对ConvertView的复用
2).Bitmap对象的复用
在ListView与GridView等显示大量图片的控件里,需要使用LRU的机制来缓存处理好的Bitmap
3).池(PooL),线程池
4).避免在onDraw方法里面执行对象的创建
5.类似onDraw等频繁调用的方法,一定需要注意避免在这里做创建对象的操作,因为他会迅速增加内存的使用,而且很容易引起频繁的gc,甚至是内存抖动。
四.Recycle(回收)
内存的回收
http://blog.csdn.net/hewence1/article/details/39233085
修改对象引用类型:
1).注意临时Bitmap对象的及时回收
2).引用类型:
引用分为四种级别,这四种级别由高到低依次为:强引用>软引用>弱引用>虚引用。
强引用(strong reference)
如:Object object=new Object(),object就是一个强引用了。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
软引用(SoftReference)
只有内存不够时才回收,常用于缓存;当内存达到一个阀值,GC就会去回收它;
弱引用(WeakReference)
弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference)
"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
软引用和弱引用的应用实例:
注意:对于SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,现在已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,所以下面的内容可以选择忽略。
在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。
五.Review(检查)
onLowMemory()与onTrimMemory()
内存蟹肉的常见的情况:
http://blog.csdn.net/u010198148/article/details/51649852
http://www.aichengxu.com/java/17381.htm
http://www.cnblogs.com/qianxudetianxia/p/3645106.html
什么是内存泄漏
定义了的变量没使用,就是内存泄漏了。
Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。
据统计,94%得OOM异常都是由于内存泄露引发的。所以,解决内存泄露是我们Android程序员必须面对的话题。
要解决MainActivity的内存泄漏问题,只需把非静态的Thread匿名类定义成静态的内部类就行了(静态的内部类不会持有外部类的一个隐式引用):
内存泄漏1:静态Activities(static Activities)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
LeakCanary检
静态变量在整个应用的内存里只保存一份,一旦创建就不会释放该变量的内存,直到整个应用都销毁才会释放static静态变量的内存.
为什么?
在上面代码中,我们声明了一个静态的Activity变量并且在TextView的OnClick事件里引用了当前正在运行的Activity实例,所以如果在activity的生命周期结束之前没有清除这个引用,则会引起内存泄漏。因为声明的activity是静态的,会常驻内存,如果该对象不清除,则垃圾回收器无法回收变量。
怎么解决?
最简单的方法是在onDestory方法中将静态变量activity置空,这样垃圾回收器就可以将静态变量回收。
- Activity Context被传递到其他实例中,这可能导致自身被引用而发生泄漏。
- 解决:对于大部分非必须使用Activity Context的情况(创建Dialog的Context必须是Activity Context),应该使用Application Context。
内存泄漏2:静态View
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
LeakCanary检测到的内存泄漏
为什么?
上面代码看似没有问题,在Activity里声明一个静态变量view,然后初始化,当Activity生命周期结束了内存也释放了,但是LeakCanary却显示出现了内存泄漏,为什么?问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。
怎么解决?
在onDestroy方法里将静态变量置空。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
内存泄漏3:非静态内部类内存泄露
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
使用LeakCanary检测到的内存泄漏:
为什么?
非静态内部类会持有外部类的引用,在上面代码中内部类持有Activity的引用,因此inner会一直持有Activity,如果Activity生命周期结束没有清除这个引用,这样就发生了内存泄漏。
怎么解决?
因为非静态内部类隐式持有外部类的强引用,所以我们将内部类声明成静态的就可以了。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
内存泄漏4:匿名内部类内存泄漏
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
使用LeakCanary检测到的内存泄漏:
为什么?
上面代码在activity中创建了一个匿名类AsyncTask,匿名类和非静态内部类相同,会持有外部类对象,这里也就是activity,因此如果你在Activity里声明且实例化一个匿名的AsyncTask对象,则可能会发生内存泄漏,如果这个线程在Activity销毁后还一直在后台执行,那这个线程会继续持有这个Activity的引用从而不会被GC回收,直到线程执行完成。
怎么解决?
自定义静态AsyncTask类,并且让AsyncTask的周期和Activity周期保持一致,也就是在Activity生命周期结束时要将AsyncTask cancel掉。
内存泄漏5:Handler
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
为什么?
当Android Application启动以后,framework会首先帮助我们完成UI线程的消息循环,也就是在UI线程中,Loop、MessageQueue、Message等等这些实例已经由framework帮我们实现了。所有的Application主要事件,比如Activity的生命周期方法、Button的点击事件都包含在这个Message里面,这些Message都会加入到MessageQueue中去,所以,UI线程的消息循环贯穿于整个Application生命周期,所以当你在UI线程中生成Handler的实例,就会持有Loop以及MessageQueue的引用。并且在Java中非静态内部类和匿名内持有外部类的引用,而静态内部类则不会持有外部类的引用。
- 在Java中,非静态(匿名)内部类会引用外部类对象。而静态内部类不会引用外部类对象。
- 当Activity Finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity(从以上原因可知道)。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
怎么解决?
可以由上面的结论看出,产生泄漏的根源在于匿名类持有Activity的引用,
1.因此可以自定义Handler和Runnable类并声明成静态的内部类,来解除和Activity的引用。
2.把消息对象从消息队列移除就行了。
3.用弱引用
内存泄漏6:Thread
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
为什么?
同AsyncTask一样,这里就不过多赘述。
怎么解决?
和Handler的处理是一样的
Virtual Machine (DVM)会为所有活跃的threads在运行时系统中保持一个硬引用,这会导致threads一直处于运行状态,垃圾收集器将永远不可能回收它.
内存泄漏7:Timer Tasks
代码如下:
MainActivity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
为什么?
这里内存泄漏在于Timer和TimerTask没有进行Cancel,从而导致Timer和TimerTask一直引用外部类Activity。
怎么解决?
在适当的时机进行Cancel。
内存泄漏8. 监听注册和反注册:evnbus和事件的监听,广播的监听
.BroadcastReceiver对象
原因:没有取消注册。
直接:getContext().unregisterReceiver(receiver);
即可.
注册与反注册:
内存泄漏9:数据库Cursor没有关闭
总结:解决内存泄漏最好的办法:程序的结构MVC分层做好了 就会有比较少的这些handler和thread的内存泄露
可以把要做的业务逻辑和UI变化都放到 一个任务栈里 ,启一个service来遍历这个栈,这样逻辑上清晰内存泄露的杂事也少些1、使用静态内部类/匿名类,不要使用非静态内部类/匿名类.非静态内部类/匿名类会隐式的持有外部类的引用,外部类就有可能发生泄漏。而静态内部类/匿名类不会隐式的持有外部类引用,外部类会以正常的方式回收,如果你想在静态内部类/匿名类中使用外部类的属性或方法时,可以显示的持有一个弱引用。2、不要以为Java永远会帮你清理回收正在运行的threads.在上面的代码中,我们很容易误以为当Activity结束销毁时会帮我们把正在运行的thread也结束回收掉,但事情永远不是这样的!Java threads会一直存在,只有当线程运行完成或被杀死掉,线程才会被回收。所以我们应该养成为thread设置退出逻辑条件的习惯。Dialog对象:使用isFinishing()判断Activity是否退出。才可以showDialog
- Bitmap没调用recycle()
- 资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们
- 构造Adapter时,没有使用缓存的 convertView
- 集合容器对象没清理造成的内存泄露(尤其是static的集合,需要clear清理)
- WebView对象没有销毁。它的destory()函数来销毁它
解决内存泄漏的工具LeakCanary:
在本篇中,楼主用LeakCanary来对内存泄漏进行检测。LeakCanary是非常好用的第三方库用来进行内存泄漏检测,感兴趣的朋友可以去查阅LeakCanary使用方法,使用它来监测App中的内存泄漏。
leakcanary 原理:http://www.jianshu.com/p/5ee6b471970e
Android的内存优化涉及的知识面还有很多:内存管理的细节,垃圾回收的工作原理,如何查找内存泄漏等等都可以展开讲很多。OOM是内存优化当中比较突出的一点,尽量减少OOM的概率对内存优化有着很大的意义。
http://blog.csdn.net/hewence1/article/details/39004301
http://blog.csdn.net/hewence1/article/details/39233085
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0920/3478.html
- Android 内存优化OOM 秒变大神 内存泄漏_ 性能优化(四)
- Android 性能优化(三)布局优化 秒变大神
- Android 性能优化(五)ANR 秒变大神
- Android 性能优化 (二)数据库优化 秒变大神
- Android性能优化-内存泄漏
- Android性能优化 --- 内存泄漏
- Android性能优化-内存泄漏(上)
- Android性能优化-内存泄漏(下)
- 性能优化(内存泄漏)
- 性能优化(内存泄漏)
- Android内存优化 OOM
- Android内存优化--OOM
- Android性能优化与内存泄漏分析
- Android性能优化-内存泄漏1
- Android性能优化-内存泄漏2
- Android性能优化: 常见内存泄漏分析
- Android性能优化-内存泄漏(一)
- Android性能优化--防止内存泄漏
- Unity3D IPV6的处理
- 移动Oracle数据文件(Windows操作记录)
- 02-angularJs指令
- c++仿函数重载
- 一个留着自己看的json模版
- Android 内存优化OOM 秒变大神 内存泄漏_ 性能优化(四)
- angular-ui-bootstrap-modal必须要说的几个点
- ZOJ2748-Free Kick
- 安卓的异步下载(ASYNCHTTPCLIENT以及VOLLEY)
- STL源码剖析-序列式容器之list和slist
- Mysql基础操作简单整理
- vscode 格式化json
- 机器学习笔记
- com.alibaba.fastjson.JSONArray cannot be cast to com.alibaba.fastjson.JSONObject