目录
一、内存
1.1 Android中的沙盒机制
1.2 物理内存
1.3 kswapd
二、幽灵刽子手LMK (Low Memory Killer)
2.1 Android进程种类
2.1.1 前台进程(foreground)
2.1.2 可见进程(visible)
2.1.3 次要服务(secondary server)
2.1.4 桌面进程(home app)
2.1.6 内容供应节点(content provider)
2.1.7 空进程(empty)
2.2 内存警戒值
2.2.1 alter minfree修改
2.3 源码分析
2.4 资源配置
2.5 时机
三、 onLowMemory()与onTrimMemory()
四、 如何评估 App 的内存占用
4.1 RSS(Resident Set Size):App 完全负责
4.2 PSS(Proportional Set Size):App 按比例负责
4.3 USS(Unique Set Size):App 无责
4.4 PSS的查看
五、与windows内存区别
参考文献
通常情况下我们说的内存是指手机的RAM物理内存,启动一个Android程序时,会启动一个Dalvik VM进程,系统会给它分配固定的内存空间(16M,32M不定),这块内存空间会映射到RAM上某个区域。
**然后这个Android程序就会运行在这块空间上。Java里会将这块空间分成Stack栈内存和Heap堆内存。stack里存放对象的引用,heap里存放实际对象数据。**
- 寄存器(Registers读音:[ˈrɛdʒɪstɚ])
- 速度最快的存储场所,因为寄存器位于处理器内部,所以在程序中我们无法控制。
- 栈(Stack)
- 存放基本类型的对象和引用,但是对象本身不存放在栈中,而是存放在堆中。
- (Heap)
- 在堆上分配内存的过程称作 内存动态分配过程。在java中堆用于存放由new创建的对象和数组。堆中分配的内存,由java虚拟机自动垃圾回收器(GC)来管理(可见我们要进行的内存优化主要就是对堆内存进行优化)。堆是不连续的内存区域(因为系统是用链表来存储空闲内存地址,自然不是连续的),堆大小受限于计算机系统中有效的虚拟内存(32bit系统理论上是4G)
- 静态存储区/方法区(Static Field)
- 是指在固定的位置上存放应用程序运行时一直存在的数据,java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量。
- 常量池(Constant Pool)
- 顾名思义专门存放常量的。注意 String s = "java"中的“java”也是常量。JVM虚拟机为每个已经被转载的类型维护一个常量池。常量池就是该类型所有用到地常量的一个有序集合包括直接常量(基本类型,String)和对其他类型、字段和方法的符号引用
在程序运行中会创建对象,如果未合理管理内存,比如不及时回收无效空间就会造成内存泄露,严重的话可能导致使用内存超过系统分配内存,即内存溢出OOM,导致程序卡顿甚至直接退出
由于Android是建立在Linux系统之上的,所以Android系统继承了Linux的 类Unix继承进程隔离机制与最小权限原则,并且在原有Linux的进程管理基础上对UID的使用做了改进,形成了Android应用的”沙箱“机制。
简单点说就是在Android的世界中每一个应用相当与一个Linux中的用户,他们相互独立,不能相互共享与访问,(这也就解释了Android系统中为什么需要进程间通信),正是**由于沙盒机制的存在最大程度的保护了应用之间的安全,但是也带来了每一个应用所分配的内存大小是有限制的问题**。
设备的物理内存被分为很多页(Page),每页 4KB。不同的页用来做不同的事情:
【物理内存】
橘色的是已使用页,黄色的是缓存页(数据在磁盘上有备份,所以 Cache Pages 是可以被回收的),绿色的是空闲页
这是一个 2G 内存的手机,X 轴表示使用时间,Y 轴表示内存使用情况。
随着打开的应用越来越多,Used Pages 也越来越多,而 Cached Pages 和 Free Pages 则越来越少。当 Free Pages 低于 kswapd 的阈值时,Linux 内核就会通过 kswapd 进程对 Cached Pages 进行回收。
当应用再次访问 Cached Pages 上的内容时,就需要从磁盘上重新加载。如果 Cached Pages 太少的话,设备就可能死机:
【kswapd】
所以,在 Android 上我们有个机制叫 Low Memory Killer,当 Cached Pages 太少时,就会被触发。它的工作方式是挑一个进程杀掉,然后该进程占用的所有内存都会被回收。
Android是一个多任务系统,也就是说可以同时运行多个程序,这个大家应该很熟悉。一般来说,启动运行一个程序是有一定的时间开销的,因 此为了加快运行速度,当你退出一个程序时,Android并不会立即杀掉它,这样下次再运行该程序时,可以很快的启动。
随着系统中保留的程序越来越多,内 存肯定会出现不足,这个时候Android系统开始挥舞屠刀杀程序。这里就有一个很明显的问题,杀谁?
Android系统中杀程序 的这个刽子手被称作"LowMemory Killer",它是在Linux内核中实现的。
它实现了一个机制: 剩余内存小于应用定义的APP_MEM值,开始查看adj值列表,kill相应程序
【Cached 也是最先被 LMK 杀掉的进程列表】
如上图所示,当已用内存超过 LMK 阈值时,LMK 将从 Cached 列表底部开始杀进程。如果可用内存还是不足,那么就按照上表一种向上杀,直到 SystemServer,此时手机会直接重启。
所以,你可以想象 LMK 在低内存手机上的情景:
【LMK 在低内存手机上的情景】
如上图所示,LMK 将一直处于活跃状态,具体表现就是黑屏、桌面重启,应用打不开等等
Android将程序的 重要性分成以下几类,按照重要性依次降低的顺序:
名称 oom_adj 解释
FOREGROUD_APP 0 前台程序,可以理解为你正在使用的程序
VISIBLE_APP 1 用户可见的程序
SECONDARY_SERVER 2 后台服务,比如说QQ会在后台运行服务
HOME_APP 4 HOME,就是主界面
HIDDEN_APP 7 被隐藏的程序
CONTENT_PROVIDER 14 内容提供者,
EMPTY_APP 15 空程序,既不提供服务,也不提供内容
其中每个程序都会有一个oom_adj值,这个值越小,程序越重要,被杀的可能性越低。
目前正在屏幕上显示的进程和一些系统进程。举例来说,Dialer,Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。我们最不希望终止的进程就是前台进程。
进程正在运行一个与用户交互的Activity ,它的onResume()方法被调用
进程有一正在运行的BroadcastReceiver,它的onReceive()方法正在执行
进程有一个Service,并且在Service的某个回调函数(onCreate()、onStart()、或onDestroy())内有正在执行的代码
进程有一个Service,被调用startforegound()要求到前台
进程有一个Service,该Service对应的Activity正在与用户交互
可见进程是一些不再前台,但用户依然可见的进程,举个例来说:widget、输入法等,都属于visible。这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止(你肯定不希望时钟、天气,新闻等widget被终止,那它们将无法同步,你也不希望输入法被终止,否则你每次输入时都需要重新启动输入法)
目前正在运行的一些服务(主要服务,如拨号等,是不可能被进程管理终止的,故这里只谈次要服务),举例来说:谷歌企业套件,Gmail内部存储,联系人内部存储等。这部分服务虽然属于次要服务,但很一些系统功能依然息息相关,我们时常需要用到它们,所以也太希望他们被终止
即launcher,保证在多任务切换之后,可以快速返回到home界面而不需重新加载launcher
即是后台进程(background),就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。当程序显示在屏幕上时,他所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按home,不是按back),程序就驻留在后台,成为后台进程(background)。后台进程的管理策略有多种:有较为积极的方式,一旦程序到达后台立即终止,这种方式会提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多的保留后台程序,虽然可能会影响到单个程序的运行速度,但在再次启动已启动的程序时,速度会有所提升。这里就需要用户根据自己的使用习惯找到一个平衡点
没有程序实体,进提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。在终止进程时,这类程序应该有较高的优先权
没有任何东西在内运行的进程,有些程序,比如BTE,在程序退出后,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。这部分进程无疑是应该最先终止的。
除了上述程序重要性分类之外,Android系统还维护着另外一张表,这张表是一个对应关系,以N1为例:
oom_adj 内存警戒值( 以4K为单位)
0 1536
1 2048
2 4096
7 5120
14 5632
15 6144
这个表是定义了一个对应关系,每一个警戒值对应了一个重要性值,当系统的可用内存低于某个警戒值时,就杀掉所有大于该警戒值对应的重要性值的程序。 比如说,当可用内存小于6144 * 4K = 24MB时,开始杀所有的EMPTY_APP,当可用内存小于5632 * 4K = 22MB时,开始杀所有
的CONTENT_PROVIDER和EMPTY_APP。
上面这张对应表是由两个文件组成的:
/sys/module/lowmemorykiller/parameters/adj和/sys/module/lowmemorykiller/parameters/minfree。
alter minfreee就是修改/sys/module/lowmemorykiller/parameters/minfree这个文件的,举例来说,如果把最后一项改为32 * 1024,那么当可用内存小于128MB是,就开始杀所有的EMPTY_APP。
Low Memory Killer的源代码在kernel/drivers/staging/android/lowmemorykiller.c中
```
module_init(lowmem_init);
module_exit(lowmem_exit);
```
模块加载和退出的函数,主要的功能就是register_shrinker和unregister_shrinker结构体lowmem_shrinker。主要是将函数lowmem_shrink注册到shrinker链表里,在mm_scan调用。
下面详细的介绍这个函数:
```
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)的进程进行分析是否释放。
```
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。
```
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中。
```
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信息,杀掉该进程
阈值表可以通过/sys/module/lowmemorykiller/parameters/adj和/sys/module/lowmemorykiller/parameters/minfree进行配置,例如在init.rc中:
```
# 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/
```
# Set init its forked children's oom_adj.
write /proc/1/oom_adj -16
```
Low memory killer的基本原理我们应该弄清了,正如我前面所说的,进程omm_adj的大小跟进程的类型以及进程被调度的次序有关。进程的类型,可以在ActivityManagerService中清楚的看到:
```
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;
```
ActivityManagerService定义各种进程的oom_adj,CORE_SERVER_ADJ代表一些核心的服务的omm_adj,数值为-12,由前面的分析可知道,这类进程永远也不会被杀死。
在init.rc中:
```
# 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
```
打开程序或者有程序进入后台时都会执行updateOomAdjLocked()函数:
```
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;
}
```
以上就是android内存管理机制的内容了,在一些设备内存比较低的情况下,我们可以对其内存进行优化,从而让我们的设备运行的更加流畅
Android用户可以随意在不同的应用之间进行快速切换。
为了让background的应用能够迅速的 切换到forground,每一个background的应用都会占用一定的内存。
Android系统会根据当前的系统的内存使用情况,决定回收部分 background的应用内存。如果background的应用从暂停状态直接被恢复到forground,能够获得较快的恢复体验,如果 background应用是从Kill的状态进行恢复,相比之下就显得稍微有点慢
- onLowMemory():
- Android 系统提供了一些回调来通知当前应用的内存使用情况,通常来说,当所有的background应用都被kill掉的时候,forground应用会收到 onLowMemory()的回调。
- 在这种情况下,需要尽快释放当前应用的非必须的内存资源,从而确保系统能够继续稳定运行。
Application.onLowMemory()
Activity.OnLowMemory()
Fragement.OnLowMemory()
Service.OnLowMemory()
ContentProvider.OnLowMemory()
- onTrimMemory(int):
- Android 系统从4.0开始还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调
- 同时在这个回调里面 会传递以下的参数,代表不同的内存使用情况,收到onTrimMemory()回调的时候,需要根据传递的参数类型进行判断,合理的选择释放自身的一些内 存占用,一方面可以提高系统的整体运行流畅度,另外也可以避免自己被系统判断为优先需要杀掉的应用
Application.onTrimMemory()
Activity.onTrimMemory()
Fragement.OnTrimMemory()
Service.onTrimMemory()
ContentProvider.OnTrimMemory()
onTrimMemory的参数:
- 后台时可能收到
- TRIM_MEMORY_UI_HIDDEN:你的应用程序的所有UI界面被隐藏了,即用户点击了Home键或者Back键退出应用,导致应用的UI界面完全不可见。这个时候应该释放一些不可见的时候非必须的资源
- 当程序正在前台运行的时候,可能会接收到从onTrimMemory()中返回的下面的值之一:
- TRIM_MEMORY_RUNNING_MODERATE:你的应用正在运行并且不会被列为可杀死的。但是设备此时正运行于低内存状态下,系统开始触发杀死LRU Cache中的Process的机制。
- TRIM_MEMORY_RUNNING_LOW:你的应用正在运行且没有被列为可杀死的。但是设备正运行于更低内存的状态下,你应该释放不用的资源用来提升系统性能。
- TRIM_MEMORY_RUNNING_CRITICAL:你 的应用仍在运行,但是系统已经把LRU Cache中的大多数进程都已经杀死,因此你应该立即释放所有非必须的资源。如果系统不能回收到足够的RAM数量,系统将会清除所有的LRU缓存中的进 程,并且开始杀死那些之前被认为不应该杀死的进程,例如那个包含了一个运行态Service的进程。
- 当应用进程退到后台正在被Cached的时候,可能会接收到从onTrimMemory()中返回的下面的值之一:
- TRIM_MEMORY_BACKGROUND: 系 统正运行于低内存状态并且你的进程正处于LRU缓存名单中最不容易杀掉的位置。尽管你的应用进程并不是处于被杀掉的高危险状态,系统可能已经开始杀掉 LRU缓存中的其他进程了。你应该释放那些容易恢复的资源,以便于你的进程可以保留下来,这样当用户回退到你的应用的时候才能够迅速恢复。
- TRIM_MEMORY_MODERATE: 系统正运行于低内存状态并且你的进程已经已经接近LRU名单的中部位置。如果系统开始变得更加内存紧张,你的进程是有可能被杀死的。
- TRIM_MEMORY_COMPLETE: 系统正运行于低内存的状态并且你的进程正处于LRU名单中最容易被杀掉的位置。你应该释放任何不影响你的应用恢复状态的资源
之前提到,设备的物理内存被分为很多页(Page),Linux Kernel 将会持续跟踪每个进程使用的 Pages,所以只要对进程使用的 Pages 进行计数即可:
【物理内存追踪1】
但实际情况远比这要复杂的多,因为有些 Pages 是进程间共享的:
【物理内存追踪2】
针对共享内存占用的分化,由此派生了四种Terms计算方式:
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS
比如下图所示两个进程共享,那就负责一半。如果三个进程共享,那就负责三分之一:
【物理内存追踪4】
实际上,至少需要系统级别才能知道 RSS 与 USS 的情况。所以通常都是使用 PSS 来计算,这还可以避免多记或者少记 Shared Pages。你可以使用:
```
adb shell dumpsys meminfo -s [process]
```
命令来查看一个进程的 PSS 使用情况:
【PSS 使用情况】
在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方面,区别于 Windows的内存管理。主要特点是,无论物理内存有多大,Linux都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。而Windows是只在需要内存时,才为应用程序分配内存,并不能充分利用大容量的内存空间。换句话说,每增加一些物理内存,Linux都将能充分利用起来,发挥了硬件投资带来的好处,而Windows只将其做为摆设,即使增加8GB甚至更大
其实我们在用安卓手机的时候不用太在意剩余内存,Android上的应用是java,当然需要虚拟机,而android上的应用是带有独立虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机。
其实和java的垃圾回收机制类似,系统有一个规则来回收内存。进行内存调度有个阀值,只有低于这个值系统才会按一个列表来关闭用户不需要的东西。当然这个值默认设置得很小,所以你会看到内存老在很少的数值徘徊。但事实上他并不影响速度。相反加快了下次启动应用的速度。这本来就是 android标榜的优势之一,如果人为去关闭进程,没有太大必要。特别是使用自动关进程的软件。
为什么内存少的时候运行大型程序会慢呢,原因是:在内存剩余不多时打开大型程序时会触发系统自身的调进程调度策略,这是十分消耗系统资源的操作,特别是在一个程序频繁向系统申请内存的时候。这种情况下系统并不会关闭所有打开的进程,而是选择性关闭,频繁的调度自然会拖慢系统。
进程管理软件有无必要呢?
有的。就是在运行大型程序之前,你可以手动关闭一些进程释放内存,可以显著的提高运行速度。
但一些小程序完全可交由系统自己管理。那么如果不关程序是不是会更耗电。android的应用在被切换到后台时,它其实已经被暂停了,并不会消耗cpu资源只保留了运行状态。所以为什么有的程序切出去重进会到主界面。但是一个程序如果想要在后台处理些东西,如音乐播放,它就会开启一个服务。服务可在后台持续运行,所以在后台耗电的也只有带服务的应用了。我们可以把带服务的进程用进程管理软件关闭就可以了。没有带服务的应用在后台是完全不耗电的没有必要关闭。这种设计本来就是一个非常好的设计,下次启动程序时会更快,因为不需要读取界面资源,何必要关掉他们抹杀这个android的优点呢
- [Android内存管理分析总结](https://www.jianshu.com/p/8b1d9c86fa84)
- [【Android 性能优化】—— 详解内存优化的来龙去脉](https://blog.csdn.net/qq_23191031/article/details/63685756)