性能优化(二):内存优化基础知识

1.Java的对象生命周期
  1. 创建(Create
    为对象分配存储空间
    开始构造对象
    从超类到子类对static成员进行初始化
    超类成员变量按顺序初始化,递归调用超类的构造函数
    子类成员变量按顺序初始化,子类构造方法调用

  2. 应用(In use)
    对象至少被一个强引用持有。

  3. 不可见(Invisible)
    当一个对象处于不可见阶段,说明程序本身不再持有该对象的任何引用,但是这些引用可能还存在着。一般具体指程序的执行已经超过该对象的作用域了。

  4. 不可达(Unreachable)
    该对象不再被任何强引用所持有。

  5. 收集(Collected)
    finalize()方法的调用时机。

  6. 终结(Finalized)
    对象运行完finalize()方法后仍处于不可达状态,则进入终结阶段,等待垃圾回收器对该对象空间进行回收。

  7. 对象空间重新分配(Deallocated)
    垃圾回收器对该对象所占用的内存空间进行回收或者再分配,则该对象彻底消失。

2.Android内存分配与回收机制
Android内存回收机制.jpg

新生代区域分为eden区、S0和S1区,S1和S2(合称Survivor区)是两块大小相等并且可以互换角色的空间。(1)对象创建后在eden区;(2)执行GC后,如果对象还存活会进入S0或S1区;(3)每一个GC,存活的对象年龄会相应增加,达到一定年龄会进入老年代;(4)最后累积一定时间再移动到持久代区域。

3.Java的四种引用
  • 强引用 如果一个对象具有强引用,它不会被垃圾回收器回收。即使当前内存不足也不会回收它,而是抛出OOM。

  • 软引用
    软引用在内存充足的时候,不会被垃圾回收器回收,只有在内存不足时,会被垃圾回收器回收。

  • 弱引用
    弱引用不管内存是否充足,都会被垃圾回收器回收。

  • 虚引用
    形同虚设,在任何时候都可能被垃圾回收器回收。

4.垃圾回收算法
  1. 标记清除算法
    标记清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。

    • 位置不连续,产生碎片
    • 效率略低
    • 两遍扫描
      标记清除算法.jpg
  2. 复制算法
    将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就是将还存活的对象复制到另一块上面,然后再把已使用的内存空间一次性清理掉。

    • 实现简单、运行高效
    • 没有内存碎片
    • 利用率只有一半
      复制算法.jpg
  3. 标记整理算法
    完成标记后,不是直接清理可回收对象,而是将存活对象都向一段移动,然后清理掉端边界以外的内存。

    • 没有内存碎片
    • 效率偏低
    • 两遍扫描、指针需要调整
      标记整理算法.jpg

分代收集算法
核心思想是根据对象存活的生命周期将内存划分为若干不同区域。

新生代:复制算法(因为每次垃圾回收都要回收大部分对象,需要复制操作少)

老年代:标记整理算法(每次回收只回收少量对象)

5.App内存组成以及限制

Android给每个App分配一个VM,让App运行在Dalvik上,这样即使App崩溃也不会影响到系统。系统给VM分配了一定的内存大小,App可以申请使用的内存大小不能超过此硬性逻辑限制,就算物理内存富余,如果应用超过VM最大内存,就会出现内存溢出crash。

由程序控制操作的内存空间在heap上,分为java heapsize和native heapsize

  • Java申请的内存在vm heap上,所以如果java申请的内存大小超过VM的逻辑内存限制,就会出现内存溢出的异常
  • native层内存申请不受其限制,受native process对内存大小的限制
6.Android进程优先级

进程的优先级从高到低,它们的oom_adj值也是从小到大:

  1. Foreground process(前台进程)
  2. Visible process(可见进程)
  3. Service process(服务进程)
  4. Background process(后台进程)
  5. Empty process(空进程)
7.内存三大问题
  1. 内存抖动
    短时间内有大量的对象被创建和回收的现象。

  2. 内存泄漏
    本该回收的对象因为某些原因不能回收。

  3. 内存溢出
    OOM,使用内存大于申请内存。

8.Android内存泄漏常见常见及解决方案
  1. 资源对象未关闭
    对于资源对象不再使用时,应该立即调用它的close()将其关闭,然后再置为null。例如Bitmap 等资源未关闭会造成内存泄漏,此时我们应该在Activity销毁时及时关闭。

  2. 注册对象未注销
    BroadcastReceiver、EventBus等未注销导致内存泄漏,我们应该在Activity销毁时及时注销。

  3. 类的静态变量持有大对象
    尽量避免静态变量存储数据,特别是大数据对象,建议使用数据库等存储。

  4. 单例造成的内存泄漏
    优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装,然后在使用到的地方从弱引用中获取Context,如果获取不到直接return即可。

  5. 非静态内部类的静态实例
    静态实例的生命周期和应用一样长,非静态内部类会持有外部类的引用,使得该静态实例一直持有Activity的引用,导致内存泄漏。我们可以将该内部类设为静态内部类。

  6. Handler引起的内存泄漏
    Handler允许我们发送延时消息,如果延时期间用户关闭了Activity,那么该Activity就会泄漏。

    这个泄漏是因为Message会持有handler,而又因为Java的特性,非静态内部类会持有外部类,使得 Activity会被Handler持有,这样最终导致了了Activity泄漏。

    解决该问题的最有效方法是:将Handler定义为静态内部类,在内部类中持有Activity的弱引用,并及时移除所有消息。(被弱引用关联的对象只能存活到下一次垃圾回收之前,被销毁的Activity会被回收内存)

     private static class SafeHandler extends Handler {
        private WeakReference<HandlerActivity> ref;
        public SafeHandler(HandlerActivity activity) {
            this.ref = new WeakReference(activity);
         }
       
         @Override
        public void handleMessage(final Message msg) {
            HandlerActivity activity = ref.get();
            if (activity != null) {
               activity.handleMessage(msg);
            } 
        }
    }
    

    并且再在Activity.onDestory()前移除消息,加一层保障

    @Override
    protected void onDestroy() {
      safeHandler.removeCallbacksAndMessages(null);
      super.onDestroy();
    }
    
  7. 容器中的对象没清理造成的内存泄漏
    退出程序之前,将集合里的东西clear并置为null。

  8. WebView内存泄漏
    WebView都存在内存泄漏的问题,在应用中只要使用一次WebView,内存就不会被释放掉。我们可以为 WebView开启一个独立的进程,使用AIDL与应用的主进程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,达到正常释放内存的目的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容