Android单排上王者系列之-Android内存

2017-01-13 19:12:43来源:CSDN作者:study_zhxu人点击

第七城市

Android内存

垃圾回收器的职责

1、分配内存

2、确保任何能被引用的对象保留在内存中

3、回收不能通过引用关系找到的对象的内存

相关概念

垃圾回收GC

垃圾回收器中有一个进程来做上面的这些事情,这个进程查找对象引用的关系并释放内存,这个进程就是常说的GC

Heap和Stack

  • Heap内存称为堆内存,指Java运行环境用来分配给对象和JRE类的内存,是引用的内存空间

  • Stack内存称为栈内存,是对Thread而言的,它保存线程中方法中短期存在的变量和Heap中对象的引用等

  • GC针对的是Heap内存,因为Stack内存相当于是随用随销的。

GC ROOT

GC Root可以理解为Heap内存之外的对象,通常包含如下

  • System Class 系统Class Loader加载的类,如Java运行环境中rt.jar类

  • Thread运行的线程

  • JNI中的本地全局变量

  • 同步监控器 任何调用wait()或notify()方法,或者是synchronized的东西

  • Java本地实例 运行在Thread的stack中方法创建的对象

JVM内存区域


  • Young Generation

新生代,所有new的对象,该区域的内存管理使用小GC

分为三部分:Enden,Survivor 0和Survivor 1部分

  • Old Generation
    老年区,新生代中执行小粒度的GC幸存下来的老对象,该区域内存管理使用大GC
  • Permanent Generation
    持久代,包含应用的类/方法信息以及JRE库的类和方法信息
  • GC流程

    Android系统启动

    1.启动电源以及系统启动

    当电源按下时,引导芯片代码开始从预定义的地方(固化在ROM)开始执行,加载引导程序到RAM中,开始执行

    2.引导程序

    引导程序是Android程序运行开始前的一个小程序,引导程序是运行的第一个程序,主要是针对主板和芯片的。引导程序不是Android操作系统的一部分。

    引导程序执行分为两个阶段,第一阶段:检测外部的RAM以及加载对第二段有用的程序。第二阶段:引导程序设置网络、内存等。

    这些都是运行内核必要的,为了达到特殊的目的,引导程序可以根据配置参数或者输入数据设置内核。

    3.内核

    内核启动时设置缓存、被保护的存储器、计划列表、加载驱动。当内核完成系统设置后它首先在系统文件中寻找init文件,然后启动root进程或者系统的第一个进程。

    4.init进程

    init是第一个进程,它是root进程的父进程。init进程有两个责任,一是挂载目录,二是运行init.rc脚本。

    5.Zygote

    在Java中,不同的虚拟机实例会为不同的应用分配不同的内存。如果Android系统为每一个应用启动不同的虚拟机就会消耗大量的内存以及时间。为此Android系统创造了Zygote,Zygote让虚拟机共享代码、低内存占用和最小时间启动称为可能。Zygote是一个虚拟器进程在系统引导的时候启动。Zygote预加载以及初始化核心类库。在Java虚拟机中,每一个实例都有它自己的核心类库文件和堆对象的copy。

    ServiceManager和Zygote进程是Android的基础,Zygote进程起来了才会建立真正的Android运行空间。

    Zygote建立完成后利用Socket通讯,接收ActivityManagerService的请求,fork应用程序。

    6.系统服务System Server

    Android的所有服务循环框架都是建立在SystemServer上的,其中通过AddService来添加服务到ServiceManager中。

    7.流程

    Linux系统通过init在启动若干守护进程后,就启动了Android的runtime和Zygote,Zygote再启动虚拟机、系统服务,系统服务再启动完本地服务后,有启动若干的Android服务,并完成ServiceManager的注册工作,最后整个系统启动完成。其中Zygote孵化器为各个进程以复制的方式用最小的代价实现了虚拟机。

    内存泄露

    Android中导致内存泄露的原因有单例导致的内存泄露,内部类导致的内存泄露,匿名内部类导致的内存泄露,静态内部类导致的内存泄露等。

    1.单例导致的内存泄露

    创建单例类,代码如下

    public class SingleManager {        private Context mContext ;        private static SingleManager instance ;        //初始化需要Context上下文对象        private SingleManager (Context context){            this.mContext = context ;        }           public static SingleManager getInstance(Context context){            if(instance == null){                synchronized(SingleManager.class){                    if(instance == null){                        instance = new SingleManager(context);                    }                }            }            return instance ;        }    }

    在activity中调用,调用方式如下

    public class MainActivity extends AppCompatActivity {        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            //调用,直接传入this对象            SingleManager.getInstance(this);        }    }

    配置好LeakCanary,然后直接点击返回按钮,LeakCanary会报异常,如图

    通过LeakCanary可以看到,报出了内存泄露的异常,是由于单例引起的内存泄露。

    在activity中使用单例时传入的参数是this对象,该this是activity,当点击返回按钮退出activity时,activity本应该被销毁,但是因为该activity被单例引用,所以不会被销毁,导致activity不会被回收,这就导致了内存泄露,那么我们如何避免这种情况产生呢。内存泄露是由于activity没有没回收引起的,所以我们可以不引用activity,可以直接引用application对象。代码修改如下。

    public class SingleManager {        private Context mContext ;        private static SingleManager instance ;        //初始化需要Context上下文对象        private SingleManager (Context context){            this.mContext = context.getApplicationContext() ;        }           public static SingleManager getInstance(Context context){            if(instance == null){                synchronized(SingleManager.class){                    if(instance == null){                        instance = new SingleManager(context);                    }                }            }            return instance ;        }    }

    通过在单例中将context对象转成application对象就可以解决该问题了。无论传入的是activity还是application,在单例中我们使用的都是application对象。

    2.Handler导致的内存泄露

    在Java中非静态内部类和匿名内部类都持有外部类对象,所以如果在activity中创建非静态内部类或者匿名内部类,所以如果activity被关闭但是内部类还被其他对象持有时,activity不会被销毁,这就导致了内存泄露。具体示例如下

    public class MainActivity extends AppCompatActivity {        private Handler handler = new Handler(){            @Override            public void handleMessage(Message msg) {                switch(msg.what) {                    case 0:                        //TODO                        break;                    case 1:                        //TODO                        break;                    default:                        break;                }            }        };        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            handler.sendEmptyMessage(0);        }    }

    在activity中创建一个handler匿名内部类,在onCreate()方法中调用sendEmptyMessage()方法,系统会发送消息到消息队列中,该消息包含一个target,该target对象是activity中匿名handler对象,这时候如果消息队列中消息过多,该消息暂时没有被处理,同时activity被关闭,这时就会引起内存泄露。

    如何解决该问题呢?

    可以通过弱引用WeakReference来解决该问题。

    修改代码如下

    public class MainActivity extends AppCompatActivity {        private static class MyHandler extends Handler {            //使用弱引用            WeakReference<MainActivity> wrf ;            public MyHandler (MainActivity activity) {                if(activity != null) {                    wrf = new WeakReference<MainActivity>(activity) ;                }            }            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);                if(wrf == null)return ;                //使用弱引用来获取activity                MainActivity activity = wrf.get();            }        }        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            MyHandler handler = new MyHandler(this) ;            handler.sendEmptyMessage(0);        }        @Override        protected void onDestroy() {            super.onDestroy();            MyApplication.getRefWatcher().watch(this);        }    }

    3.内部类导致的内存泄露

    内部类引起的内存泄露示例如下

    public class MainActivity extends AppCompatActivity {        //内部类        class InnerClass {        }        //静态内部类对象        private static InnerClass innerClass ;        //创建内部类对象        private void createInnerClass(){            innerClass = new InnerClass() ;        }        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            createInnerClass();        }    }

    activity中创建内部类,并且使用静态对象持有该内部类对象,被static修饰的变量声明周期是整个源程序的生命周期,与activity生命周期无关,所以当activity被关闭时innerClass变量不会被清空,依然存在内存中,innerClass变量持有内部类对象,间接的持有外部类的MainActivity对象。导致MainActivity不会被回收,导致出现内存泄露,LeakCanary检测如下

    4.匿名内部类导致的内存泄露

    匿名内部类导致内存泄露如下示例如下

    public class MainActivity extends AppCompatActivity {        void startAsyncTask() {            new AsyncTask<Void, Void, Void>() {                @Override                protected Void doInBackground(Void... params) {                    while(true);                }            }.execute();        }        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            startAsyncTask();        }    }

    在activity创建匿名内部类,匿名内部类中执行线程,当activity退出时,线程依然在执行,这个匿名内部类依然存在,匿名内部类只有外部类activity,这就导致activity不会被回收,导致内存泄露。

    LeakCanary检测如下

    第七城市

    最新文章

    123

    最新摄影

    微信扫一扫

    第七城市微信公众平台