Android内存管理机制详解 柔光的暖阳◎ 2022-07-28 01:15 162阅读 0赞 ## 与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) ## ### 执行条件 ### 剩余内存小于应用定义的APP\_MEM值,开始查看adj值列表,kill相应程序。 ### 实现机制 ### Low Memory Killer的源代码在kernel/drivers/staging/android/lowmemorykiller.c中 **\[cpp\]** [view plain][] [copy][view plain] 1. module\_init(lowmem\_init); 2. 3. module\_exit(lowmem\_exit); 模块加载和退出的函数,主要的功能就是register\_shrinker和unregister\_shrinker结构体lowmem\_shrinker。主要是将函数lowmem\_shrink注册到shrinker链表里,在mm\_scan调用。 下面详细的介绍这个函数: **\[cpp\]** [view plain][] [copy][view plain] 1. **for** (i = 0; i < array\_size; i++) \{ 2. **if** (other\_file < lowmem\_minfree\[i\]) \{ 3. min\_adj = lowmem\_adj\[i\]; 4. **break**; 5. \} 6. \} other\_file, 系统的空闲内存数,根据上面的逻辑判断出,low memory killer需要对adj高于多少(min\_adj)的进程进行分析是否释放。 **\[cpp\]** [view plain][] [copy][view plain] 1. **if** (nr\_to\_scan <= 0 || min\_adj == OOM\_ADJUST\_MAX + 1) \{ 2. lowmem\_print(5, "lowmem\_shrink %d, %x, return %d\\n", 3. nr\_to\_scan, gfp\_mask, rem); 4. **return** rem; 5. \} 判断,系统当前的状态是否需要进行low memory killer。 **\[cpp\]** [view plain][] [copy][view plain] 1. for\_each\_process(p) \{ 2. **struct** mm\_struct \*mm; 3. **struct** signal\_struct \*sig; 4. **int** oom\_adj; 5. task\_lock(p); 6. mm = p->mm; 7. sig = p->signal; 8. **if** (!mm || !sig) \{ 9. task\_unlock(p); 10. **continue**; 11. \} 12. oom\_adj = sig->oom\_adj; 13. **if** (oom\_adj < min\_adj) \{ 14. task\_unlock(p); 15. **continue**; 16. \} 17. tasksize = get\_mm\_rss(mm); 18. task\_unlock(p); 19. **if** (tasksize <= 0) 20. **continue**; 21. **if** (selected) \{ 22. **if** (oom\_adj < selected\_oom\_adj) 23. **continue**; 24. **if** (oom\_adj == selected\_oom\_adj && 25. tasksize <= selected\_tasksize) 26. **continue**; 27. \} 28. selected = p; 29. selected\_tasksize = tasksize; 30. selected\_oom\_adj = oom\_adj; 31. lowmem\_print(2, "select %d (%s), adj %d, size %d, to kill\\n", 32. p->pid, p->comm, oom\_adj, tasksize); 33. \} 对每个sig->oom\_adj大于min\_adj的进程,找到占用内存最大的进程存放在selected中。 **\[cpp\]** [view plain][] [copy][view plain] 1. **if** (selected) \{ 2. **if** (fatal\_signal\_pending(selected)) \{ 3. pr\_warning("process %d is suffering a slow death\\n", 4. selected->pid); 5. read\_unlock(&tasklist\_lock); 6. **return** rem; 7. \} 8. lowmem\_print(1, "send sigkill to %d (%s), adj %d, size %d\\n", 9. selected->pid, selected->comm, 10. selected\_oom\_adj, selected\_tasksize); 11. force\_sig(SIGKILL, selected); 12. rem -= selected\_tasksize; 13. \} 发送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][view plain] 1. \# Write value must be consistent with the above properties. 2. 3. write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15 4. write /proc/sys/vm/overcommit\_memory 1 5. write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144 6. 7. class\_start default 进程oom\_adj同样可以进行设置,通过write /proc/<PID>/oom\_adj ,在init.rc中,init进程的pid为1,omm\_adj被配置为\-16,永远不会被杀死。 **\[plain\]** [view plain][] [copy][view plain] 1. \# Set init its forked children's oom\_adj. 2. 3. write /proc/1/oom\_adj -16 Low memory killer的基本原理我们应该弄清了,正如我前面所说的,进程omm\_adj的大小跟进程的类型以及进程被调度的次序有关。进程的类型,可以在ActivityManagerService中清楚的看到: **\[java\]** [view plain][] [copy][view plain] 1. **static** **final** **int** EMPTY\_APP\_ADJ; 2. **static** **final** **int** HIDDEN\_APP\_MAX\_ADJ; 3. **static** **final** **int** HIDDEN\_APP\_MIN\_ADJ; 4. **static** **final** **int** HOME\_APP\_ADJ; 5. **static** **final** **int** BACKUP\_APP\_ADJ; 6. **static** **final** **int** SECONDARY\_SERVER\_ADJ; 7. **static** **final** **int** HEAVY\_WEIGHT\_APP\_ADJ; 8. **static** **final** **int** PERCEPTIBLE\_APP\_ADJ; 9. **static** **final** **int** VISIBLE\_APP\_ADJ; 10. **static** **final** **int** FOREGROUND\_APP\_ADJ; 11. **static** **final** **int** CORE\_SERVER\_ADJ = -12; 12. **static** **final** **int** SYSTEM\_ADJ = -16;<span style="font-family:Calibri;font-size:14px;"></span> ActivityManagerService定义各种进程的oom\_adj,CORE\_SERVER\_ADJ代表一些核心的服务的omm\_adj,数值为-12,由前面的分析可知道,这类进程永远也不会被杀死。 在init.rc中: **\[plain\]** [view plain][] [copy][view plain] 1. \# Define the oom\_adj values for the classes of processes that can be 2. \# killed by the kernel. These are used in ActivityManagerService. 3. setprop ro.FOREGROUND\_APP\_ADJ 0 4. setprop ro.VISIBLE\_APP\_ADJ 1 5. setprop ro.HOME\_APP\_ADJ 1 6. setprop ro.PERCEPTIBLE\_APP\_ADJ 2 7. setprop ro.HEAVY\_WEIGHT\_APP\_ADJ 3 8. setprop ro.SECONDARY\_SERVER\_ADJ 4 9. setprop ro.BACKUP\_APP\_ADJ 5 10. setprop ro.HIDDEN\_APP\_MIN\_ADJ 7 11. setprop ro.EMPTY\_APP\_ADJ 15 12. 13. \# Define the memory thresholds at which the above process classes will 14. \# be killed. These numbers are in pages (4k). 15. setprop ro.FOREGROUND\_APP\_MEM 2048 16. setprop ro.VISIBLE\_APP\_MEM 3072 17. setprop ro.HOME\_APP\_MEM 3072 18. setprop ro.PERCEPTIBLE\_APP\_MEM 4096 19. setprop ro.HEAVY\_WEIGHT\_APP\_MEM 4096 20. setprop ro.SECONDARY\_SERVER\_MEM 10240 21. setprop ro.BACKUP\_APP\_MEM 10240 22. setprop ro.HIDDEN\_APP\_MEM 10240 23. setprop ro.EMPTY\_APP\_MEM 14336 24. 25. \# Write value must be consistent with the above properties. 26. \# Note that the driver only supports 6 slots, so we have combined some of 27. \# the classes into the same memory level; the associated processes of higher 28. \# classes will still be killed first. 29. write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15 30. 31. write /proc/sys/vm/overcommit\_memory 1 32. write /proc/sys/vm/min\_free\_order\_shift 4 33. write /sys/module/lowmemorykiller/parameters/minfree 2048,3072,4096,10240,10240,14336 34. 35. \# Set init its forked children's oom\_adj. 36. write /proc/1/oom\_adj -16 ActivityManagerService.java 打开程序或者有程序进入后台时都会执行updateOomAdjLocked()函数: **\[java\]** [view plain][] [copy][view plain] 1. **final** **boolean** updateOomAdjLocked() \{ 2. **boolean** didOomAdj = **true**; 3. **final** ActivityRecord TOP\_ACT = resumedAppLocked(); 4. **final** ProcessRecord TOP\_APP = TOP\_ACT != **null** ? TOP\_ACT.app : **null**; 5. 6. **if** (**false**) \{ 7. RuntimeException e = **new** RuntimeException(); 8. e.fillInStackTrace(); 9. Slog.i(TAG, "updateOomAdj: top=" + TOP\_ACT, e); 10. \} 11. 12. mAdjSeq++; 13. 14. // Let's determine how many processes we have running vs. 15. // how many slots we have for background processes; we may want 16. // to put multiple processes in a slot of there are enough of 17. // them. 18. **int** numSlots = HIDDEN\_APP\_MAX\_ADJ - HIDDEN\_APP\_MIN\_ADJ + 1; 19. **int** factor = (mLruProcesses.size()-4)/numSlots; 20. **if** (factor < 1) factor = 1; 21. **int** step = 0; 22. **int** numHidden = 0; 23. 24. // First try updating the OOM adjustment for each of the 25. // application processes based on their current state. 26. **int** i = mLruProcesses.size(); 27. **int** curHiddenAdj = HIDDEN\_APP\_MIN\_ADJ; 28. **while** (i > 0) \{ 29. i--; 30. ProcessRecord app = mLruProcesses.get(i); 31. //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj); 32. **if** (updateOomAdjLocked(app, curHiddenAdj, TOP\_APP)) \{ 33. **if** (curHiddenAdj < EMPTY\_APP\_ADJ 34. && app.curAdj == curHiddenAdj) \{ 35. step++; 36. **if** (step >= factor) \{ 37. step = 0; 38. curHiddenAdj++; 39. \} 40. \} 41. **if** (app.curAdj >= HIDDEN\_APP\_MIN\_ADJ) \{ 42. **if** (!app.killedBackground) \{ 43. numHidden++; 44. **if** (numHidden > MAX\_HIDDEN\_APPS) \{ 45. Slog.e(TAG, "No longer want " + app.processName 46. + " (pid " + app.pid + "): hidden \#" + numHidden); 47. EventLog.writeEvent(EventLogTags.AM\_KILL, app.pid, 48. app.processName, app.setAdj, "too many background"); 49. app.killedBackground = **true**; 50. Process.killProcessQuiet(app.pid); 51. \} 52. \} 53. \} 54. \} **else** \{ 55. didOomAdj = **false**; 56. \} 57. \} 58. // If we return false, we will fall back on killing processes to 59. // have a fixed limit. Do this if a limit has been requested; else 60. // only return false if one of the adjustments failed. 61. **return** ENFORCE\_PROCESS\_LIMIT || mProcessLimit > 0 ? **false** : didOomAdj; 62. \}<span style="font-family:Calibri;font-size:14px;"> </span> 以上就是android内存管理机制的内容了,在一些设备内存比较低的情况下,我们可以对其内存进行优化,从而让我们的设备运行的更加流畅。 转载地址:[http://blog.csdn.net/chaihuasong/article/details/8289367][http_blog.csdn.net_chaihuasong_article_details_8289367] [view plain]: http://blog.csdn.net/chaihuasong/article/details/8289367# [http_blog.csdn.net_chaihuasong_article_details_8289367]: http://blog.csdn.net/chaihuasong/article/details/8289367
相关 Java内存管理:垃圾回收机制详解 在Java编程中,内存管理是非常重要的一部分,尤其是垃圾回收(Garbage Collection, GC)机制。下面将详细解释这个过程。 1. **什么是垃圾?** 小咪咪/ 2024年09月11日 06:48/ 0 赞/ 21 阅读
相关 Java内存管理:垃圾回收机制详解 Java的内存管理主要是通过垃圾回收(Garbage Collection,GC)机制来实现的。以下是垃圾回收机制的详细解释: 1. **垃圾定义**: 垃圾是不再使用 矫情吗;*/ 2024年09月11日 06:00/ 0 赞/ 18 阅读
相关 Android内存管理机制 应用程序的内存分配和垃圾回收都是由Android虚拟机完成的 Android 5.0以下:Dalvik虚拟机 5.0及以上:ART虚拟机 一、Android内 以你之姓@/ 2024年03月22日 22:05/ 0 赞/ 53 阅读
相关 android内存管理机制(一) 无意中在MIUI看到的文章,感觉不错,转了过来。 原文如下: 最近看到很多机油发帖抱怨内存太小进程杀不掉。首先要表示,这个帖子是从百度贴吧转来的,主要针对正常的安卓机,像里 骑猪看日落/ 2022年07月28日 01:15/ 0 赞/ 163 阅读
相关 Android内存管理机制详解 与windows内存区别 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个 柔光的暖阳◎/ 2022年07月28日 01:15/ 0 赞/ 163 阅读
相关 详解JVM内存管理与垃圾回收机制1 - 内存管理 > 本文为转载文章,原文链接:[https://www.jianshu.com/p/f8d71e1e8821][https_www.jianshu.com_p_f8d71e1e 痛定思痛。/ 2022年04月14日 07:17/ 0 赞/ 292 阅读
相关 (一)Android的内存管理机制 Google在Android的官网上有这样一篇文章,初步介绍了Android是如何管理应用的进程与内存分配:[http://developer.android.com/t 绝地灬酷狼/ 2022年04月12日 10:44/ 0 赞/ 240 阅读
相关 详解JVM内存管理与垃圾回收机制1 - 内存管理 前言 Java应用程序是运行在JVM上的,得益于JVM的内存管理和垃圾收集机制,开发人员的效率得到了显著提升,也不容易出现内存溢出和泄漏问题。但正是因为开发人员把内存的控 「爱情、让人受尽委屈。」/ 2022年04月11日 02:20/ 0 赞/ 300 阅读
还没有评论,来说两句吧...