下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

Android内存管理机制是怎样的?有什么用?

作者:课课家教育     来源: http://www.kokojia.com点击数:1289发布时间: 2019-03-21 10:34:52

标签: Android管理软件Windows

  Android采取了一种有别于Linux的进程管理策略,有别于Linux的在进程活动停止后就结束该进程,Android把这些进程都保留在内存中,直到系统需要更多内存为止。这些保留在内存中的进程通常情况下不会影响整体系统的运行速度,并且当用户再次激活这些进程时,提升了进程的启动速度。

linux系统与android系统不同,linux是一个类unix操作系统,android是一个人们喜爱的常用的操作系统。

Android内存管理机制原理_Android_内存管理软件_课课家

  一、linux与Windows的内存区别

  在Linux中,经常会发现很少空闲内存,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,实则不然。这是Linux内存管理的一个优秀特性,在这方面,区别于Windows的内存管理。主要特点是,无论物理内存有多大,Linux都将其充分利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。而Windows是只在需要内存时,才为应用程序分配内存,并不能充分利用大容量的内存空间。换句话说,每增加一些物理内存,Linux都将能充分利用起来,发挥了硬件投资带来的好处,而Windows只将其作为摆设,即使增加8GB甚至更大。

  二、android内存的意义

  很多人用安卓手机时都不会太在意剩余内存。Android上的应用是java,当然需要虚拟机,而android上的应用是带有独立虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机。其实和java的垃圾回收机制类似,系统有一个规则以回收内存。进行内存调度有个阀值,只有低于这个值时,系统才会按一个列表来关闭用户不用的程序。这个值默认设置得很小,所以你会看到内存老在很小的数值徘徊。实际上,它不会影响速度,相反地,加快了下次启动应用的速度。这本来就是android的一个优势,所以,没有太大的必要人为地去关闭进程,特别是使用自动关进程的软件。内存少时,运行大型程序会变慢的原因是:当内存剩余不多了,打开大型程序时会触发系统自身的调进程调度策略,这种操作十分消耗系统资源,特别是在一个程序频繁向系统申请内存时。这种情况下系统不会关闭所有打开的进程,而是选择性关闭,频繁的调度就会拖慢系统。

  三、进程管理软件

  进程管理软件是很有必要的。运行大型程序之前,你可以人为地关闭一些进程,以释放内存,显著的提高运行速度。但一些小程序完全可交由系统来管理。那么,不关闭程序是不是更耗电?其实,android的应用被切换到后台时已经暂停了,只保留了运行状态,并不会消耗cpu资源。这就是为什么有的程序切出去重进会回到主界面的原因。但一个程序如果要在后台处理些东西,如音乐播放,它就会开启一个服务。服务可在后台持续运行,所以在后台耗电的只有带服务的应用。你可以通过进程管理软件把带服务的进程关闭。没必要关闭没有带服务的应用,因为它们在后台是完全不耗电的。这种设计本来就非常好的,下次启动程序时会更快,因为不需要读取界面资源,我们又何必要关掉它们,抹杀了android的这个设计优点呢!

  四、Android进程种类

  1.前台进程(foreground)

  目前正在屏幕上显示的进程和一些系统进程。例如:Dialer,Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。我们最不希望终止的进程就是前台进程。

  2.可见进程(visible)

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

  3.桌面进程(home app)

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

  4.次要服务(secondary server)

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

  5.后台进程(hidden)

  后台进程(background),也就是通常意义上理解的启动后被切换到后台的进程,如浏览器、阅读器等。当程序显示在屏幕上时,它所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按home,不是按back),程序就驻留在后台,成为后台进程(background)。

  后台进程的管理策略有多种:有较为积极的方式,程序一到达后台立即终止,这种方式可以提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多地保留后台程序,虽然可能会影响到单个程序的运行速度,但再次启动已启动的程序时,速度会有所提升。这就要根据用户的使用习惯自己找到一个平衡点。

  6.内容供应节点(content provider)

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

  7.空进程(empty)

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

  五、幽灵刽子手LMK(Low Memory Killer)

  1.执行条件

  剩余内存小于应用定义的APP_MEM值,开始查看adj值列表,kill相应程序。

  2.实现机制

  Low Memory Killer的源代码在kernel/drivers/staging/android/lowmemorykiller.c中

  [cpp]view

  plaincopy

  module_init(lowmem_init);

  module_exit(lowmem_exit);

  模块加载和退出的函数,主要的功能就是register_shrinker和unregister_shrinker结构体lowmem_shrinker。主要是将函数lowmem_shrink注册到shrinker链表里,在mm_scan调用。

  以下详细地介绍一下这个函数:

  [cpp] view

  plain copy

  for (i = 0; i < array_size; i++) {

  if (other_file < lowmem_minfree[i]) {

  min_adj = lowmem_adj[i];

  break;

  }

  }

  other_file, 系统的空闲内存数,根据上面的逻辑判断出,low memory killer需要对adj高于多少(min_adj)的进程进行分析是否释放。

  [cpp] view

  plain copy

  if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {

  lowmem_print(5, "lowmem_shrink %d, %x, return %d\\n",

  nr_to_scan, gfp_mask, rem);

  return rem;

  }

  判断系统当前状态是否需要进行low memory killer。

  [cpp] view

  plain copy

  for_each_process(p) {

  struct mm_struct *mm;

  struct signal_struct *sig;

  int oom_adj;

  task_lock(p);

  mm = p->mm;

  sig = p->signal;

  if (!mm || !sig) {

  task_unlock(p);

  continue;

  }

  oom_adj = sig->oom_adj;

  if (oom_adj < min_adj) {

  task_unlock(p);

  continue;

  }

  tasksize = get_mm_rss(mm);

  task_unlock(p);

  if (tasksize <= 0)

  continue;

  if (selected) {

  if (oom_adj < selected_oom_adj)

  continue;

  if (oom_adj == selected_oom_adj &&

  tasksize <= selected_tasksize)

  continue;

  }

  selected = p;

  selected_tasksize = tasksize;

  selected_oom_adj = oom_adj;

  lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\\n",

  p->pid, p->comm, oom_adj, tasksize);

  }

  对每个sig->oom_adj大于min_adj的进程,找到占用内存最大的进程存放在selected中。

  [cpp] view

  plain copy

  if (selected) {

  if (fatal_signal_pending(selected)) {

  pr_warning("process %d is suffering a slow death\\n",

  selected->pid);

  read_unlock(&tasklist_lock);

  return rem;

  }

  lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\\n",

  selected->pid, selected->comm,

  selected_oom_adj, selected_tasksize);

  force_sig(SIGKILL, selected);

  rem -= selected_tasksize;

  }

  发送SIGKILL信息,杀掉该进程。

  了解了其机制和原理后,会发现它的实现非常简单,与标准的Linux OOM机制类似,只是实现方式略有差别。标准Linux的OOM Killer机制在mm/oom_kill.c中实现,且会被__alloc_pages_may_oom调用(在分配内存时,即mm/page_alloc.c中)。oom_kill.c最主要的一个函数是out_of_memory,它选择一个bad进程Kill,Kill的方法也是通过发送SIGKILL信号。在out_of_memory中通过调用select_bad_process来选择一个进程Kill,选择的依据在badness函数中实现,基于多个标准来给每个进程评分,评分最高的被选中并Kill。一般而言,占用内存越多,oom_adj就越大,也就越有可能被选中。

  资源配置

  阈值表可以通过/sys/module/lowmemorykiller/parameters/adj和/sys/module/lowmemorykiller/parameters/minfree进行配置,例如在init.rc中:

  [plain] view

  plain copy

  # Write value must be consistent with the above properties.

  write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15

  write /proc/sys/vm/overcommit_memory 1

  write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144

  class_start default

  进程oom_adj同样可以进行设置,通过write /proc//oom_adj ,在init.rc中,init进程的pid为1,omm_adj被配置为-16,永远不会被杀死。

  [plain] view

  plain copy

  # Set init its forked children's oom_adj.

  write /proc/1/oom_adj -16

  弄清楚了Low memory killer的基本原理,进程omm_adj的大小跟进程的类型以及进程被调度的次序有关。进程的类型,可以在Activity Manager Service中清楚的看到:

  [java] view

  plain copy

  static final int EMPTY_APP_ADJ;

  static final int HIDDEN_APP_MAX_ADJ;

  static final int HIDDEN_APP_MIN_ADJ;

  static final int HOME_APP_ADJ;

  static final int BACKUP_APP_ADJ;

  static final int SECONDARY_SERVER_ADJ;

  static final int HEAVY_WEIGHT_APP_ADJ;

  static final int PERCEPTIBLE_APP_ADJ;

  static final int VISIBLE_APP_ADJ;

  static final int FOREGROUND_APP_ADJ;

  static final int CORE_SERVER_ADJ = -12;

  static final int SYSTEM_ADJ = -16;

  Activity Manager Service定义各种进程的oom_adj,CORE_SERVER_ADJ代表一些核心的服务的omm_adj,数值为-12,由上述分析可知,这类进程永远也不会被杀死。

  在init.rc中:

  [plain] view

  plain copy

  # Define the oom_adj values for the classes of processes that can be

  # killed by the kernel. These are used in ActivityManagerService.

  setprop ro.FOREGROUND_APP_ADJ 0

  setprop ro.VISIBLE_APP_ADJ 1

  setprop ro.HOME_APP_ADJ 1

  setprop ro.PERCEPTIBLE_APP_ADJ 2

  setprop ro.HEAVY_WEIGHT_APP_ADJ 3

  setprop ro.SECONDARY_SERVER_ADJ 4

  setprop ro.BACKUP_APP_ADJ 5

  setprop ro.HIDDEN_APP_MIN_ADJ 7

  setprop ro.EMPTY_APP_ADJ 15

  # Define the memory thresholds at which the above process classes will

  # be killed. These numbers are in pages (4k).

  setprop ro.FOREGROUND_APP_MEM 2048

  setprop ro.VISIBLE_APP_MEM 3072

  setprop ro.HOME_APP_MEM 3072

  setprop ro.PERCEPTIBLE_APP_MEM 4096

  setprop ro.HEAVY_WEIGHT_APP_MEM 4096

  setprop ro.SECONDARY_SERVER_MEM 10240

  setprop ro.BACKUP_APP_MEM 10240

  setprop ro.HIDDEN_APP_MEM 10240

  setprop ro.EMPTY_APP_MEM 14336

  # Write value must be consistent with the above properties.

  # Note that the driver only supports 6 slots, so we have combined some of

  # the classes into the same memory level; the associated processes of higher

  # classes will still be killed first.

  write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15

  write /proc/sys/vm/overcommit_memory 1

  write /proc/sys/vm/min_free_order_shift 4

  write /sys/module/lowmemorykiller/parameters/minfree 2048,3072,4096,10240,10240,14336

  # Set init its forked children's oom_adj.

  write /proc/1/oom_adj -16

  Activity Manager Service.java

  打开程序、或有程序进入后台时,都会执行update OomAdj Locked()函数:

  [java] view

  plain copy

  final boolean updateOomAdjLocked() {

  boolean didOomAdj = true;

  final ActivityRecord TOP_ACT = resumedAppLocked();

  final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; 

  if (false) {

  RuntimeException e = new RuntimeException();

  e.fillInStackTrace();

  Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);

  }

  mAdjSeq++;

  // Let's determine how many processes we have running vs.

  // how many slots we have for background processes; we may want

  // to put multiple processes in a slot of there are enough of

  // them.

  int numSlots = HIDDEN_APP_MAX_ADJ - HIDDEN_APP_MIN_ADJ + 1;

  int factor = (mLruProcesses.size()-4)/numSlots;

  if (factor < 1) factor = 1;

  int step = 0;

  int numHidden = 0; 

  // First try updating the OOM adjustment for each of the

  // application processes based on their current state.

  int i = mLruProcesses.size();

  int curHiddenAdj = HIDDEN_APP_MIN_ADJ;

  while (i > 0) {

  i--;

  ProcessRecord app = mLruProcesses.get(i);

  //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj);

  if (updateOomAdjLocked(app, curHiddenAdj, TOP_APP)) {

  if (curHiddenAdj < EMPTY_APP_ADJ

  && app.curAdj == curHiddenAdj) {

  step++;

  if (step >= factor) {

  step = 0;

  curHiddenAdj++;

  }

  }

  if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {

  if (!app.killedBackground) {

  numHidden++;

  if (numHidden > MAX_HIDDEN_APPS) {

  Slog.e(TAG, "No longer want " + app.processName

  + " (pid " + app.pid + "): hidden #" + numHidden);

  EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,

  app.processName, app.setAdj, "too many background");

  app.killedBackground = true;

  Process.killProcessQuiet(app.pid);

  }

  }

  }

  } else {

  didOomAdj = false;

  }

  }

  // If we return false, we will fall back on killing processes to

  // have a fixed limit. Do this if a limit has been requested; else

  // only return false if one of the adjustments failed.

  return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj;

  }

  进程管理软件正确的用途是杀那些出错的程序、会导致死机有BUG的进程以及疑似病毒进程等,而不是一味地追求内存空得多程序在内存里放着。android内存管理机制就是在一些设备内存比较低的情况下,对其内存进行优化,从而使我们的设备运行更流畅。

  了解了android内存的管理机制之后,我们会发现,只要按照上述文中的资源配置部分进行配置,它的实现其实很简单。

赞(0)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程