APP性能优化-Memory
APP性能优化-稳定性(crash率)
APP性能优化-包体压缩
APP性能优化-CPU
APP性能优化-UI
APP性能优化-流畅度
GC内存浅析
JVM区域总体分两类,heap区和非heap区。
heap区又分为:Eden Space(伊甸园)
、Survivor Space(幸存者区)
、Old Gen(老年代)
非heap区又分:Code Cache(代码缓存区)
、Perm Gen(永久代)
、Jvm Stack(java虚拟机栈)
、Local Method Statck(本地方法栈)
内存优化这里只讨论head区,当对象在enden区被new出来时,如果enden区内存不够时,那么将触发minor gc
,此gc将enden仍然活跃的对象拷贝到survivor 缓冲区,survivor 区内存在经历多次回收仍然存活时将被拷贝到old区。当old区内存达到一定阈值时将进行Major GC
,调用system.gc()触发的就是Major GC。
内存引发的问题
OOM
:当我们努力将各种crash修复之后,发现最终我们没法解决的crash基本都是内存引发的OOM,特别对于一些早期的android设备,此问题尤为严重
APP卡顿
:频繁创建、释放内存以及调用system.gc()导致的内存抖动进而引发gc,由于gc时的stop the world
特性导致app卡顿
在实际开发过程中面临最直观的几个关于内存的问题就是:内存泄漏
、APP整体内存偏高
、内存抖动
内存泄漏
早期的时候通过dump内存获取hprof文件通过MAT来分析内存泄漏问题,目前基本上都集成# leakcanary来排查内存泄漏问题;下面列举一些常见的导致内存泄漏的例子
单例
:单例一版作用于全局,如果单例中使用了某个activity的context,那么这个context将得不到释放导致内存泄漏。fix
:在单例中使用context.getApplicationContext()来解决此类泄漏
static view
:如果一个View初始化耗费大量资源,可能会将这个view变成static加载到视图上,但是当页面结束时又没有释放view,导致activity被hook进而导致内存泄漏。fix
:在activity destory时将static view置为null
监听器
:android中很多监听器register时都持有context引用,而在activity销毁是并没有调用unregister导致内存泄漏,如SensorManager.registerListener ;fix
:在activity销毁时调用对应unregister
资源未关闭
:BroadcastReceiver,contentObserver File,Cursor,Stream,Bitmap等资源的代码在activity销毁时没有关闭;fix`:在Activity销毁时及时关闭或者注销
Inner Classes
:内部类的引用依赖外部类,当一个static变量引用了内部类而没有销毁时,那么外部类将出现内存泄漏。fix
:在销毁的时候将内部类引用置为null或者使用静态内部类
Handler
:使用handler延迟发送消息,如果此时用户退出页面,Activity将得不到回收;被传递到Handler的消息队列MessageQueue中,在Message消息没有被处理之前,Activity实例不会被销毁了,于是导致内存泄漏fix
:在activity ondestory中调用handler.removeMessages
将message移除
匿名内部类
:匿名类也维护了外部类的引用,所以内存泄漏很容易发生,常见泄漏有AsyncTask
、Thread
、TimerTask
等,这类泄漏只有在线程运行完成之后才会释放对activity引用
关于对activity引用导致的内存泄漏可以结合softReference
(GC且内存不足时被回收)、weakReference
(GC时被回收)使用避免长时间内存泄漏;
APP整体内存偏高
APP平均实用内存过高会更快接近OOM内存阈值导致最终Crash,可以使用MAT
对每个页面进行针对性内存分析,大致可以从以下几个方面着手降低平均内存:
过度绘制
:背景颜色在Resource里会被转成Drawable,同时绘制需要使用内存,过多的背景一定程度上会照成内存的增加;优化过度绘制请查看:APP性能优化-UI
布局层级
:每多使用一层布局就会多使用一份内存,优化布局层级请查看:APP性能优化-UI
数据结构
:使用StringBuffer
替代String
,第一可以提高效率,第二可以节省内存,因为String为常量,每次操作都会重新申请常量内存;使用SparseIntArray
、SparseBooleanArray
、SparseLongArray
等替换HashMap,SparseArray使用key和value两个数组分别存放数据,使用的内存远小于HashMap的节点,HashMap通过key计算hash,同时在发生hash冲突时会转成链表甚至红黑树,所以每个数据节点将包含4个字段key、hash、value、next(链表的下一个节点),这样每个节点的内存远大于SparseArray,同时扩容机制在超过当前容量的0.75(扩容因子可配置)将会触发倍数扩容同样浪费内存;
Bitmap
:当使用BitmapFactory、AssetManager或者其他方式获取Bitmap对象时,在使用完毕后及时通过bitmap.recycle()将图片资源回收
懒加载布局
:对于第一次不是必须要展示在用户面前的布局可以使用ViewStub进行占坑;Viewpager懒加载机制;在需要展示时动态加载布局,关闭关闭时及时回收布局(如一些类似弹窗的展示布局);
内存抖动
避免在需要重复调用的函数或者循环中初始化变量,可以将变量提到外部初始化只分配一次内存多次使用;如避免在自定义View的onDraw中创建Paint等
总结
关于内存问题整体优化方向为开源节流
、及时回收内存
开源
:通过在mainifest中Application中指定android:largeHeap="true"
,提高OOM内存阈值;
节流
:防止内存泄漏、优化数据结构、及时回收bitmap、减少过度绘制、减少布局层级、延迟代码调用或者布局初始化
及时回收内存
:Bitmap、View、数据等使用完毕时及时释放所占用的内存
网友评论