Android性能优化之内存篇

android的内存优化一般从以下几个方面考虑:

  • 内存泄漏
  • 内存抖动
  • Bitmap
  • 代码质量优化

内存泄漏

内存泄漏的本质:不合理的引用导致引用者对象的生命周期>被引用者对象的生命周期。当回收被引用者对象时发现该对象还在被引用状态,无法被回收,就出现了内存泄漏。

常见的内存泄漏场景:

  • 非静态匿名内部类

比如经典的在一个Activity里new一个非static 的Handler对象、在Activity里new了一个非静态的Thread或者Runnable。还有很多类似的,尤其是在Activity或者Fragment内引用一个非静态的匿名内部类时,这个类都会持有外部类,相当于持有了context,极易导致context无法被回收,然后就内存泄漏。

解决方法:我们可以把这个内部类设置为静态的;或者不要使用内部类,在外面写个类;如果涉及到Context的必须持有问题,用Application的Context,因为其生命周期较长且唯一,可以不用回收,这样就不会泄漏。

  • Static 关键字修饰的成员变量

我们知道,jvm层面static修饰的变量是放在方法区的,方法区是永久代,基本上不会发送GC,或者说其发送GC的条件非常苛刻,而且在java1.8后出现了元空间,回收更是遥遥无期,也就是说static修饰的变量的生命周期跟你的APP应用生命周期一样,如果你的static修饰的成员变量是Context或者持有Context时,那就会导致这个变量回收不掉,会莫名其妙用了很多内存,我们每次启动一个Activity都会new一个Context对象,如此反复你的内存很快就爆掉了。

解决方法:我们在写程序时不要用static修饰类似占用大资源的对象(例如Context,View)。

  • 单例模式

这个跟上面的static原理是一样的,因为单例模式是static的,其生命周期很长,所以要特别小心其持有对象导致内存泄漏。例如:常见的观察者模式+单例模式时,我们会在Activity里实现某个接口(观察者接口),然后把这个接口add到单例模式的ArrayList(被观察者),这个过程就相当于单例对象持有了一个Activity对象,如果在Activity destroy时没有把这个Activity从ArrayList里remove掉,那就造成内存泄漏了。

解决办法:单例模式里持有变量时要注意其生命周期的管理,有+也有-,这样才安全。另外在这种情况里,我们可以适当合理地使用WeakReference。

  • 集合类

当一个集合使用完后没有清空其持有的对象。例如:我们往ArrayList里add一个对象时,有的时候我们把是把一个对象add进去,但是ArrayList真正持有的是这个对象的引用,所以即使我们把add的这个对象置null,但是还会依然持有对象的引用,也就等于还持有另外对象。

解决办法:当集合不用时,要清空然后置null。例如:arrayList.clear();arrayList=null;

  • 资源对象未关闭

例如:registerBroadcast最后没有unregisterBroadcast;数据库操作的cursor没有close;stream流未关闭;Bitmap没有recycle等;

解决办法:关闭或者释放相关的信息

  • 其他情况

例如:在ListView里没有复用好View而是创建了大量的View;Webview使用没有关闭;

内存泄漏分析、跟踪、监测的工具:

  1. Android Studio的Memory;
  2. MAT(Memory Analysis Tools);
  3. Heap Viewer;
  4. Allocation Tracker;
  5. LeakCanary;

至于如何利用这些工具进行内存的分析,后续我会慢慢补上。
以上都是理论知识,也是普遍的问题存在,而且相对来说比较容易理解,大部分开发人员也熟知,真正的提升需要在实践中,所以有时间大家可以参考外面文章的同时自己多实践。


内存抖动

所谓的内存抖动就是短时间里,内存出现了反复的波动,其出现的原因主要是因为短时间内,我们写代码时创建了大量的对象,频繁地触发了GC机制回收对象,如果回收的速度赶不上你创建的速度,极有可能就OOM了,而且创建了大量的对象再回收会导致磁盘空间占用比较分散,不利于整体分配内存,会影响内存利用率和使用效率等。避免内存抖动注意以下几种:

  • 避免for循环、while循环里new了很多对象;
  • 避免View的onDraw()方法里创建对象;
  • 避免在引用api时创建了大量对象,比如java的字符串拼接api;
  • 避免创建大量的Bitmap,尽量用内存池进行管理;
  • 避免创建大量的View, 尽可能复用;
  • 其他的能够复用的尽量复用对象,用对象池进行管理;

其实内存抖动还是比较容易定位的,这一块的分析和修改也没有太大的难度,就不做过多的分析了。


Bitmap的使用

在很多的项目中,Bitmap占用的内存达到了整个App占用内存的50%,甚至更高。Bitmap的优化一般从如下几个方面考虑:

  • 避免加载过大的图片;
  • 及时释放不用的Bitmap;
    android在早期版本会把Bitmap占用的内存放在Native里,中间(大概2.2后)转到java heap层了,后面(8.0)又转到Native层了。所以什么时候释放,如何释放Bitmap占用的内存是我们要考虑的重点,例如RecyclerView、ListView、ViewPager加载View,当滑动后View不可见时Bitmap的回收。
  • Bimap缓冲池的使用,尽可能复用以前分配的内存;
    可以参考GlideBitmapPool的设计。
  • 适当的WeakRefrence使用;
    主要是利于GC回收内存。
  • 根据不同分辨率合理配置图片;
    理解不同分辨率和density下图片所占用的内存,进行图片配置。
  • 采取合理的图片压缩技术;
    主要考虑的参数有inTargetDensity,inSampleSize,inJustDecodeBounds,inPreferredConfig,inBitmap等,要理解这些参数的含义(比较简单)。
  • 利用缓存机制在业务层面根据不同的图片使用频繁度进行多缓存池+不同的缓存机制进行优化

关于Bitmap推荐一篇文章https://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg==&mid=403263974&idx=1&sn=b0315addbc47f3c38e65d9c633a12cd6&scene=21#wechat_redirect


代码质量

就是写代码过程里要注意一些小细节。

  • 尽量不用或者少用枚举,可以用IntDef/@StringDef + @interface来进行限定参数,因为枚举占内存大且容易来带类型不安全问题。
  • 当界面不可见时,适当地释放内存,避免内存占用过高同时也可避免进程被回收,因为不可见的进程内存占用越高被回收的概率越高。可以在Activity的onTrimMemory(int level)方法里做。
  • 使用Android系统自带的数据存储结构代码Java自带的,例如SparseArray取代HashMap。
  • 使用HashMap、ArrayList这些存储结构时,要适当考虑容量,尽可能避免无端扩容带来的性能浪费。
  • 利用好Java的引用类型(强软弱虚),合理选择引用类型。
  • 谨慎使用多进程。
  • 谨慎使用largeHeap。
  • 谨慎使用shareprefercnce。
  • 用好ProGuard剔除不必要的代码。
  • 选好基础数据类型,能用byte的不用int,能用int的不用long,尽量不用包装类型。
  • 注意dex文件和.so文件数量问题,加载过多的这类文件内存也会增加很多。
  • 可以考虑利用好匿名共享内存,但是要注意管理好,匿名共享内存自身是管杀不管埋的。
  • Parcelable序列化对象时不能太大,否则会爆掉导致crash。
  • 尽量选用系统资源,如String,Color,Style等。
  • Log信息输出的选择上,最后选用aop式,在正式代码里直接不把日志输出代码编译进去。
  • 优化线程的分配,过多的线程分配和线程切换浪费资源(内存+CPU)。在App开发过程中,我们经常会引入很多第三方SDK,在这些SDK里各自都可能存在多线程或者多线程池的情况,我们尽可能选择可配置线程的SDK使用,最后统一用一个线程池进行管理线程。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容