(转)应用内存优化之OnLowMemory&OnTrimMemory
1.应用内存onLowMemory& onTrimMemory优化
onLowMemory& onTrimMemory简介:
OnLowMemory是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory。
OnTrimMemory是Android 4.0之后提供的API,系统会根据不同的内存状态来回调。根据不同的内存状态,来响应不同的内存释放策略。
1.1 onLowMemory& onTrimMemory优化,需要释放什么资源?
在内存紧张的时候,会回调OnLowMemory/OnTrimMemory,需要在回调方法中编写释放资源的代码。
可以在资源紧张的时候,释放UI 使用的资源资:Bitmap、数组、控件资源。
1.2 OnLowMemory
OnLowMemory是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory。系统提供的回调有:
Application.onLowMemory()
Activity.OnLowMemory()
Fragement.OnLowMemory()
Service.OnLowMemory()
ContentProvider.OnLowMemory()
除了上述系统提供的API,还可以自己实现ComponentCallbacks,通过API注册,这样也能得到OnLowMemory回调。例如:
public static class MyCallback implements ComponentCallbacks {
@Override
public void onConfigurationChanged(Configuration arg) {
} @Override
public void onLowMemory() {
//do release operation
}
}
然后,通过Context.registerComponentCallbacks ()在合适的时候注册回调就可以了。通过这种自定义的方法,可以在很多地方注册回调,而不需要局限于系统提供的组件。
onLowMemory 当后台程序已经终止资源还匮乏时会调用这个方法。好的应用程序一般会在这个方法里面释放一些不必要的资源来应付当后台程序已经终止,前台应用程序内存还不够时的情况。
1.3 OnTrimMemory
OnTrimMemory是Android 4.0之后提供的API,系统会根据不同的内存状态来回调。系统提供的回调有:
Application.onTrimMemory()
Activity.onTrimMemory()
Fragement.OnTrimMemory()
Service.onTrimMemory()
ContentProvider.OnTrimMemory()
OnTrimMemory的参数是一个int数值,代表不同的内存状态:
TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理
TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。
TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。
TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。
以上4个是4.0增加
TRIM_MEMORY_RUNNING_CRITICAL:内存不足(后台进程不足3个),并且该进程优先级比较高,需要清理内存
TRIM_MEMORY_RUNNING_LOW:内存不足(后台进程不足5个),并且该进程优先级比较高,需要清理内存
TRIM_MEMORY_RUNNING_MODERATE:内存不足(后台进程超过5个),并且该进程优先级比较高,需要清理内存
以上3个是4.1增加
系统也提供了一个ComponentCallbacks2,通过Context.registerComponentCallbacks()注册后,就会被系统回调到。
1.4 OnLowMemory和OnTrimMemory的比较
1,OnLowMemory被回调时,已经没有后台进程;而onTrimMemory被回调时,还有后台进程。
2,OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory killer 杀进程后触发;而OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。
3,通过一键清理后,OnLowMemory不会被触发,而OnTrimMemory会被触发一次。
使用举例:
@Override
public void onTrimMemory(int level) {
Log.e(TAG, " onTrimMemory ... level:" + level);
} @Override
public void onLowMemory() {
Log.e(TAG, " onLowMemory ... ");
}
2.系统回调优化
2.1 回调原理:
在Application、 Activity、Fragement、Service、ContentProvider中都可以重写回调方法,对OnLowMemory/OnTrimMemory进行回调,在回调方法中实现资源释放的实现。
以Activity为例,在Activity源码中能够看到对于onTrimMemory的定义,因此在回调的时候重写方法即可。
public void onTrimMemory(int level) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onTrimMemory " + this + ": " + level);
mCalled = true;
mFragments.dispatchTrimMemory(level);
}
2.2 释放资源:
在onTrimMemory释放资源,释放图片、数组、缓存等资源。
@Override
public void onTrimMemory(int level) {
// TODO Auto-generated method stub
DLog.d(" onTrimMemory ... level:" + level); switch(level) {
case TRIM_MEMORY_UI_HIDDEN:
//释放资源
/*编写释放资源代码*/
} break;
}
super.onTrimMemory(level);
}
下面是释放Bitmap的示例代码片段:
// 先判断是否已经回收
if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置为null
bitmap.recycle();
bitmap = null;
}
System.gc();
list占用方法:
list.clear();然后在置空。
3.实现ComponentCallbacks
OnLowMemory除了上述系统提供的API,还可以自己实现ComponentCallbacks,通过API注册,这样也能得到OnLowMemory回调。例如:
public static class ViewComponentCallbacks implements ComponentCallbacks {
@Override
public void onConfigurationChanged(Configuration arg) {
} @Override
public void onLowMemory() {
//do release operation
}
}
注册自定义的回调类:
ViewComponentCallbacks callBacks =new ViewComponentCallbacks();
this.registerComponentCallbacks( callBacks );
回调之后,即可进行重写:
@Override
public void onLowMemory() {
// TODO Auto-generated method stub
//释放资源的方法
super.onLowMemory();
}
Android onTrimMemory方法的一些疑惑?
当你的app进程正在被cached时,你可能会接受到从onTrimMemory()中返回的下面的值之一:
- TRIM_MEMORY_BACKGROUND: 系统正运行于低内存状态并且你的进程正处于LRU缓存名单中最不容易杀掉的位置。尽管你的app进程并不是处于被杀掉的高危险状态,系统可能已经开始杀掉LRU缓存中的其他进程了。你应该释放那些容易恢复的资源,以便于你的进程可以保留下来,这样当用户回退到你的app的时候才能够迅速恢复。
- TRIM_MEMORY_MODERATE: 系统正运行于低内存状态并且你的进程已经已经接近LRU名单的中部位置。如果系统开始变得更加内存紧张,你的进程是有可能被杀死的。
- TRIM_MEMORY_COMPLETE: 系统正运行与低内存的状态并且你的进程正处于LRU名单中最容易被杀掉的位置。你应该释放任何不影响你的app恢复状态的资源。
- 这个地方讲的进程正在被cache 是什么意思呢?是指我的进程 还没有结束 正在内存中 但是因为没有执行任何操作 不占有cpu的意思吗?
此外当我们监听onTrimMemory回调方法的时候,假设我的缓存里最大的部分就是bitmap,请问就直接把bitmap从内存里抹去就可以了么? 那如果这么做,在进程重新被加载到前台来的时候,我怎么迅速恢复这些bitmap资源?
答疑1:
android进程回收主要涉及两个组件:ActivityManagerService(Ams)和lowmemorykiller。当手机内存不足时,lowmemorykiller就要开始杀进程了。但是lowmemorykiller呢只知道进程占用的内存大小,不知道进程对用户的重要性。Ams则负责管理android四大组件,当然知道进程的重要性了,所以呢还需要与Ams充分交换意见。
当app状态发生改变时,比如退到后台时,ams会对app的进程计算出一个值,即Oomadj(ams#computeOomAdjLocked),然后把这个值传给linux内核,lowmemorykiller呢就可以拿到这个值了,lowmemorykiller则就有了所有app进程的Oomadj值,即进程对用户的重要程度。当手机内存不足时,lowmemorykiller就有了足够的信息决定干掉哪个进程了。那,lowmemorykiller决定干掉哪个进程呢?这个要根据手机还有多少空闲内存,比如还有16MB空闲内存,如下lowmemorykiller.c
static short lowmem_adj[6] = {
0,
1,
6,
12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {
3 * 512, /* 6MB */
2 * 1024, /* 8MB */
4 * 1024, /* 16MB */
16 * 1024, /* 64MB */
};
16MB在lowmem_minfree第三个位置,取lowmem_adj第三个位置即6,ok所有Oomadj大于6的进程就被选中了。而lowmemorykiller不是把这些选中的进程都干掉,而是先干掉oomAdj最大而且占用内存最大的进程。如下lowmemorykiller.c#lowmem_scan:
for_each_process(tsk) {
...
oom_score_adj = p->signal->oom_score_adj;
if (oom_score_adj < min_score_adj) {
task_unlock(p);
continue;
}
tasksize = get_mm_rss(p->mm);
task_unlock(p);
if (tasksize <= 0)
continue;
if (selected) {
if (oom_score_adj < selected_oom_score_adj)
continue;
if (oom_score_adj == selected_oom_score_adj &&
tasksize <= selected_tasksize)
continue;
}
selected = p;
selected_tasksize = tasksize;
selected_oom_score_adj = oom_score_adj;
}
总之一句话:进程对用户越不重要(Oomadj值就越大),占用内存越大,进程就越容易被干掉。
应对策略:
1 ams计算出一个危险的Oomadj值会调用onTrimMemory通知app,此时app应该把不重要的内存释放掉,只要比友商app占用的内存小被lowmemorykiller干掉的概率就小。
2 当然我们也可以先把忧伤的app干掉。。。帮用户释放内存(开玩笑啦)
3 如果我们的app能预置到手机中,并且manifest设置为persistence(Oomadj=0),或者coreserver则不必担心被lowmemorykiller干掉了。不过还有可能被linux层的oomkiller干掉的。当然这也是某些手机预置一堆app导致越用越慢的一个原因,所以我们尽量把这些预置app卸载掉。
答疑2:
理解进程和组件之间的关系
一般来说,你的App在启动的时候,系统都会为你的APP创建一个进程。
比如我有一个App,其包名为com.performance.liferecord
当这个App启动的时候,我们可以通过ps命令来看其对应的进程 信息:
结果如下:
USER PID PPID VSIZE RSS WCHAN PC NAME
可以看到系统为这个进程分配的PD是32452,那么 PPID是什么呐?
使用PS命令来看:adb shell ps | grep 379
可以看到PPID的NAME是zygote64,也就是说32452这个进程是从Android的zygote64这个进程fork过来的。
那么 组件又是怎么回事呐?我们常说四大组件,Activity、Service、Broadcast、ContentProvider。这些个组件都是依附于一个进程来运行的,一个进程可以有多个Activity、Service等,也可以一个组件都没有。
Cache状态
当你的应用程序到了后台之后,会进入Cached状态,这时候进程还存在,但是组件是否存在就不一定了。
不过一般我们认为,当你按Back键回到桌面之后,如果Activity没有被释放,那么我们认为了内容泄漏,这个有兴趣可以自己看看。
以上面我提到的com.performance.liferecord这个应用为例子,当我启动这个应用之后,我们可以使用命令adb shell dumpsys meminfo来查看整个系统的状态,此时com.performance.liferecord这个进程状态为Foreground:
Foreground意味着此时你的进程处于前台进程,你的程序的界面可以被用户看到。此时我们使用adb shell dumpsys meminfo com.preformance.liferecord来看这个进程的状态:
可以看到Activity的数量为1(因为我只起了一个Activity)
当我们点击Back键的时候,我们可以使用命令adb shell dumpsys meminfo来查看 整个系统的状态,此时com.performance.liferecord这个进程状态为Cached:
Cached状态标识你的应用进程变成了后台进程,位于LRU list里面。
此时我们使用adb shell dumpsys meminfo com.performance.liferecord来看这个进程的状态:
Activity的数量妥妥变成0了,说明没有内存泄漏。此时你的进程里面,是没有任何组件的。当然也不占用cpu的资源,但是会点内存(例子中我们可以看到占用了20706kb)的内存。
将后台进程放到Cached列表里而不是直接杀掉,其好处就是下一次这个进程的某个组件(最常见的就是Activity)启动的时候,不需要再创建新的进程 ,速度杠杠的(比如就应用的热启动)。
onTrimMemory
此外当我们监听onTrimMemory回调方法的时候,假设我的缓存里最大的部分就是bitmap,请问就直接把bitmap从内存里抹去就可以了么? 那如果这么做,在进程重新被加载到前台来的时候,我怎么迅速恢复这些bitmap资源?
如答疑1所述:
对于 图片,一般采用三层缓存机制,即内存、文件、网络、释放内存后从文件中读取就好
一般的图片加载库都会提供内存/文件的缓存,即使内存中的Bitmap清除掉了,下一次从文件中读取也会很快的。
至少onTrimMemory的优点,比如你的应用Cache了20MB的Bitmap没有清除,当手机内存不足的时候,如果你没有及时释放 这些内存,那么 很可能你的进程 就被系统给杀掉了,这会你的Bitmap当然也就没了。用户下次启动你的应用的时候,我曹这货又被杀了,这手机系统有毛病吧?
是吧。。。做系统的还得背这个锅!
关于onTrimMemory的使用,可以参考博客http://androidperformance.com/2015/07/20/Android-Performance-Memory-onTrimMemory.html
(转)应用内存优化之OnLowMemory&OnTrimMemory的更多相关文章
- Android 应用内存优化 之 onLowMemory & onTrimMemory
OnLowMemory: 是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory.OnTri ...
- 应用内存优化之OnLowMemory&OnTrimMemory
1.应用内存onLowMemory& onTrimMemory优化 onLowMemory& onTrimMemory简介:OnLowMemory是Android提供的API,在系统内 ...
- Android代码内存优化建议-OnTrimMemory优化
原文 http://androidperformance.com/2015/07/20/Android代码内存优化建议-OnTrimMemory优化/ OnTrimMemory 回调是 Androi ...
- 【MDCC技术大咖秀】Android内存优化之OOM
大神分析的很全面,所以就转过来保存一份,转自:http://www.csdn.net/article/2015-09-18/2825737/1 以下为正文: Android的内存优化是性能优化中很重要 ...
- Android内存优化之OOM
内容大多都是和OOM有关的实践总结概要.理解错误或是偏差的地方,还请多包涵指正,谢谢!本人Q:1524447071 (一)Android的内存管理机制 Google在Android的官网上有这样一篇文 ...
- Android避免OOM(内存优化)
Android内存优化是性能优化很重要的一部分,而如何避免OOM又是内存优化的核心. Android内存管理机制 android官网有一篇文章 Android是如何管理应用的进程与内存分配 Andro ...
- Android代码内存优化建议-Android官方篇
转自:http://androidperformance.com/ http://developer.android.com/intl/zh-cn/training/displaying-bitmap ...
- Android 性能优化 ---- 内存优化
1.Android内存管理机制 1.1 Java内存分配模型 先上一张JVM将内存划分区域的图 程序计数器:存储当前线程执行目标方法执行到第几行. 栈内存:Java栈中存放的是一个个栈帧,每个栈帧对应 ...
- Android性能优化:手把手带你全面实现内存优化
前言 在 Android开发中,性能优化策略十分重要 本文主要讲解性能优化中的内存优化,希望你们会喜欢 目录 1. 定义 优化处理 应用程序的内存使用.空间占用 2. 作用 避免因不正确使用内 ...
随机推荐
- 去掉有序数组中重复数字 原地 leetcode java (最简单的方法)
1.利用荷兰国旗的思路,每次记住最后一个位置,遇到一个不重复的数,放在它后面,代码很简单. Given a sorted array, remove the duplicates in place s ...
- ASP.NET中设置一个定时器来定时更新 转
asp.net 定时器 比较少用, 中国红木网这是一个相当实用的功能,有了RSS博客镜像,就不需要在多处同时发布博客日志了.比如你同时在新浪上有自己的博客,又同时有自己的个人博客站点,那么你只需要在 ...
- DIY常用网站
工作: 技术: 学习: 个人十佳博客介绍:http://hedengcheng.com/?p=676
- 关于vim打开中文文件出现乱码问题
中文字符编码问题. 在你的vi配置文件(~/.vimrc)里面添加一行: set fileencodings=ucs-bom,utf-8,cp936,gb18030,latin1 意思是让vim从 这 ...
- sublime text 2 运行 python时返回EOFError: EOF when reading a line错误
其主要原因是sublime text 2中python没有与 stdin(标准输入)连接所致,解决方法也很简单,那就是安装sublimeREPL插件,然后 Tools->sublimerepl- ...
- SOA技术的进化史
SOA 是一种程序设计思想,其实早在远古时代(计算机史)它就已经出现了.无非就是把系统分解,将数据和业务逻辑部分尽量独立出来,然后以服务形式提供给另外的系统共用. 那时也有一些可以实现 SOA 的工具 ...
- 一次服务器CPU占用率高的定位分析
现象: 当前项目启动一段时间,有一个服务导致CPU使用率持续超过30% 环境:Windows 7, CPU: 8核, 内存: 8g内存 定位过程: 启动项目,查看Java进程ID 查看Event P ...
- 抓取用户openid
获取用户微信openid用户无感知情况下 传参为 appid appsecret 当前网址 session_name名称 <?php //获取微信的openid function get_wx_ ...
- 【JAVA - SSM】之MyBatis开发DAO
在SSM框架中的DAO层就是MyBatis中的Mapper,Mapper分为两部分:Mapper接口(JAVA文件)和Mapper映射文件(XML文件).DAO开发(Mapper开发)有两种方式:原始 ...
- redis学习大全
http://blog.csdn.net/menergy/article/details/17577985 http://blog.sina.com.cn/s/blog_64008ed70102uy ...