Android内存管理机制

  • Android内存的意义
  • Android进程种类
    • 前台进程(foreground)
    • 可见进程(visible)
    • 桌面进程(home app)
    • 次要服务(secondary server)
    • 后台进程(hidden)
    • 内容供应节点(content provider)
    • 空进程(empty)
  • 垃圾内存回收算法
    • 引用计数回收法(Reference Counting GC)
    • 根搜索算法
      • 标注并清理回收法(Mark and Sweep GC)
      • 标注并整理回收法(Mark and COMPACT GC)
      • 拷贝回收法(Copying GC)
    • 逐代回收法(Generational GC)
  • Android内存管理源码分析

Android内存的意义

  在使用安卓系统手机的时候我们并不用太在意所剩余内存:
  由于Android上的应用是java,所以需要虚拟机,并且Android上的应用是带有独立虚拟机的,也就是说每开一个Android应用同时就会为该应用打开一个独立的虚拟机。而Android与linux的区别在于:
  1.Linux在进程活动停止后就结束该进程,
  2.Android在进程活动停止后,并没有结束该进程,而是将进程都保留在内存中。
  所以Android和java的垃圾回收机制类似,系统有一个规则来回收内存。进行内存的调度有一个阀值,只有当可用阀值低于这个值时系统才会按该规则来关闭用户不需要的东西,但事实上他并不影响速度,相反加快了下次启动应用的速度(内存读写的高速特性)。这本来就是 android标榜的优势之一,如果人为去关闭进程,没有太大必要。至于说内存少的时候运行大型程序会变慢,那是因为:在内存剩余不多时打开大型程序时会触发系统自身的调进程调度策略,该调度是个十分消耗系统资源的操作,尤其是在一个程序频繁向系统申请内存的时候。这种情况下系统并不会关闭所有打开的进程,而是选择性关闭,频繁的调度自然会拖慢系统。

Android进程种类

前台进程(foreground)

  目前正在屏幕上显示的进程和一些系统进程,即可能用户当前正在通过该进程与系统进行交互,所以该进程为最重要的进程,除非系统的内容已经到不堪重负的情况,否则系统是不会将改进程终止的,举例来说,Dialer,Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。我们最不希望终止的进程就是前台进程。

可见进程(visible)

  可见进程是一些不再前台,但用户依然可见的进程,举个例来说:widget、输入法等,都属于visible。这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止(你肯定不希望时钟、天气,新闻等widget被终止,那它们将无法同步,你也不希望输入法被终止,否则你每次输入时都需要重新启动输入法)

桌面进程(home app)

  即launcher,保证在多任务切换之后,可以快速返回到home界面而不需重新加载launcher

后台进程(hidden)

  即是后台进程(background),就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。当程序显示在屏幕上时,他所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按home,不是按back),程序就驻留在后台,成为后台进程(background)。后台进程的管理策略有多种:有较为积极的方式,一旦程序到达后台立即终止,这种方式会提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多的保留后台程序,虽然可能会影响到单个程序的运行速度,但在再次启动已启动的程序时,速度会有所提升。这里就需要用户根据自己的使用习惯找到一个平衡点

次要服务(secondary server)

  目前正在运行的一些服务(主要服务,如拨号等,是不可能被进程管理终止的,故这里只谈次要服务),举例来说:谷歌企业套件,Gmail内部存储,联系人内部存储等。这部分服务虽然属于次要服务,但很一些系统功能依然息息相关,我们时常需要用到它们,所以也太希望他们被终止

内容供应节点(content provider)

  没有程序实体,进提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。在终止进程时,这类程序应该有较高的优先权

空进程(empty)

  没有任何东西在内运行的进程,有些程序,比如BTE,在程序退出后,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。这部分进程无疑是应该最先终止的。

垃圾内存回收算法

引用计数回收法(Reference Counting GC)

原理:即记录每个对象被引用的次数。每当创建一个新的对象,或者将其它指针指向该对象时,引用计数都会累加一次;而每当将指向对象的指针移除时,引用计数都会递减一次,当引用次数降为0时,删除对象并回收内存。采用这种算法比较出名的框架有微软的COM框架。
关系图如下:
图1 引用计数与对象实例关系图
通常对象的引用计数都会跟对象放在一起,系统在分配完对象的内存后,返回的对象指针会跳过引用计数部分。
以下示例演示了一个对象引用计数的增减方式。

Object obj1 = new Object();// obj1的引用计数为1
Object obj2 = obj1;// obj2指针指向obj1,所以obj1的引用计数为2
Object obj3 = new Object();

obj2 = null; // obj1的引用计数递减1次为1。
obj1 = obj3; // obj1的引用计数递减1次为0,可以回收其内存。

缺点:无法有效处理循环引用的问题

根搜索算法

原理:程序在运行的过程中会不停的创建新的对象并消耗内存,直到内存用光,这时如果需要再创建新的对象时,系统暂停其它组件的运行,并触发GC线程启动垃圾回收过程。该过程即:从”GC Roots”集合开始,遍历一次内存,然后保留所有可以被GC Roots直接或间接引用到的对象,而剩下的对象则都当作垃圾对待并回收。并且在根搜索算法的基础上,现代虚拟机的实现当中,垃圾搜集的算法主要分有三种:标注并清理回收法、标注并整理回收法、拷贝回收法
回收内存垃圾之前的对象引用关系
回收内存垃圾之前的对象引用关系
即当前内存中存在A、B、C、D、E、F、G、H八个对象

标注并清理回收法(Mark and Sweep GC)

1.标记:即在执行”GC Roots”遍历时,找出所有被直接或者间接引用着的对象并做上标记
2.清理:清除遍历堆中所有未被标记的对象。
3.缺点:
  3.1:效率比较低(递归与全堆对象遍历),而且在进行GC的时候,需要停止应用程序,这会导致用户体验非常差劲,尤其对于交互式的应用程序来说简直是无法接受。
  3.2:清理出来的空闲内存是不连续的
如下图2:GC线程标识出所有不能被回收的对象实例
这里写图片描述
执行完”GC Roots”遍历之后发现,只有A、B、C、D、H五个对象被“GC Roots直接或者间接引用着,其余就会被当做垃圾进行回收

标注并整理回收法(Mark and COMPACT GC)

1.标记:标注并清理回收法一样,均是遍历GC Roots,然后将存活的对象标记。
2.整理:如图2中标记的存活对象,移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。

拷贝回收法(Copying GC)
逐代回收法(Generational GC)

Android内存管理源码分析

Android源码,可以在网址:http://androidxref.com/source/xref/ 中在线浏览。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章