Android代码内存优化建议-OnTrimMemory优化

图片 7

OnTrimMemory 回调是 Android 4.0 之后提供的一个API,这个 API
是提供给开发者的,它的主要作用是提示开发者在系统内存不足的时候,通过处理部分资源来释放内存,从而避免被
Android 系统杀死。这样应用在下一次启动的时候,速度就会比较快。
本文通过问答的方式,从各个方面来讲解 OnTrimMemory
回调的使用过程和效果。想要开发高性能且用户体验良好的 Android
应用,那么这篇文章你不应该错过。

0. OnTrimMemory回调的作用?

OnTrimMemory是Android在4.0之后加入的一个回调,任何实现了ComponentCallbacks2接口的类都可以重写实现这个回调方法.OnTrimMemory的主要作用就是指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉,提高应用程序的用户体验.

Android系统会根据不同等级的内存使用情况,调用这个函数,并传入对应的等级:

  • TRIM_MEMORY_UI_HIDDEN
    表示应用程序的所有UI界面被隐藏了,即用户点击了Home键或者Back键导致应用的UI界面不可见.这时候应该释放一些资源.
    TRIM_MEMORY_UI_HIDDEN这个等级比较常用,和下面六个的关系不是很强,所以单独说.

下面三个等级是当我们的应用程序真正运行时的回调:

  • TRIM_MEMORY_RUNNING_MODERATE
    表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经有点低了,系统可能会开始根据LRU缓存规则来去杀死进程了。
  • TRIM_MEMORY_RUNNING_LOW
    表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经非常低了,我们应该去释放掉一些不必要的资源以提升系统的性能,同时这也会直接影响到我们应用程序的性能。
  • TRIM_MEMORY_RUNNING_CRITICAL
    表示应用程序仍然正常运行,但是系统已经根据LRU缓存规则杀掉了大部分缓存的进程了。这个时候我们应当尽可能地去释放任何不必要的资源,不然的话系统可能会继续杀掉所有缓存中的进程,并且开始杀掉一些本来应当保持运行的进程,比如说后台运行的服务。

当应用程序是缓存的,则会收到以下几种类型的回调:

  • TRIM_MEMORY_BACKGROUND
    表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。这个时候我们的程序在LRU缓存列表的最近位置,是不太可能被清理掉的,但这时去释放掉一些比较容易恢复的资源能够让手机的内存变得比较充足,从而让我们的程序更长时间地保留在缓存当中,这样当用户返回我们的程序时会感觉非常顺畅,而不是经历了一次重新启动的过程。
  • TRIM_MEMORY_MODERATE
    表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉的风险了。
  • TRIM_MEMORY_COMPLETE
    表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的最边缘位置,系统会最优先考虑杀掉我们的应用程序,在这个时候应当尽可能地把一切可以释放的东西都进行释放。

1.应用内存onLowMemory& onTrimMemory优化

相关文章:从Android资源角度谈Android代码内存优化

1. 哪些组件可以实现OnTrimMemory回调?

  • Application.onTrimMemory()
  • Activity.onTrimMemory()
  • Fragement.OnTrimMemory()
  • Service.onTrimMemory()
  • ContentProvider.OnTrimMemory()

onLowMemory& onTrimMemory简介:
OnLowMemory是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory。
OnTrimMemory是Android
4.0之后提供的API,系统会根据不同的内存状态来回调。根据不同的内存状态,来响应不同的内存释放策略。

0. OnTrimMemory回调的作用?

OnTrimMemory是Android在4.0之后加入的一个回调,任何实现了ComponentCallbacks2接口的类都可以重写实现这个回调方法.OnTrimMemory的主要作用就是
指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉,提高应用程序的用户体验.

Android系统会根据不同等级的内存使用情况,调用这个函数,并传入对应的等级:

  • TRIM_MEMORY_UI_HIDDEN 表示应用程序的
    所有UI界面被隐藏了,即用户点击了Home键或者Back键导致应用的UI界面不可见.这时候应该释放一些资源.TRIM_MEMORY_UI_HIDDEN这个等级比较常用,和下面六个的关系不是很强,所以单独说.

下面三个等级是当我们的应用程序真正运行时的回调:

  • TRIM_MEMORY_RUNNING_MODERATE
    表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经有点低了,系统可能会开始根据LRU缓存规则来去杀死进程了。
  • TRIM_MEMORY_RUNNING_LOW
    表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经非常低了,我们应该去释放掉一些不必要的资源以提升系统的性能,同时这也会直接影响到我们应用程序的性能。
  • TRIM_MEMORY_RUNNING_CRITICAL
    表示应用程序仍然正常运行,但是系统已经根据LRU缓存规则杀掉了大部分缓存的进程了。这个时候我们应当尽可能地去释放任何不必要的资源,不然的话系统可能会继续杀掉所有缓存中的进程,并且开始杀掉一些本来应当保持运行的进程,比如说后台运行的服务。

当应用程序是缓存的,则会收到以下几种类型的回调:

  • TRIM_MEMORY_BACKGROUND
    表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。这个时候我们的程序在LRU缓存列表的最近位置,是不太可能被清理掉的,但这时去释放掉一些比较容易恢复的资源能够让手机的内存变得比较充足,从而让我们的程序更长时间地保留在缓存当中,这样当用户返回我们的程序时会感觉非常顺畅,而不是经历了一次重新启动的过程。
  • TRIM_MEMORY_MODERATE
    表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉的风险了。
  • TRIM_MEMORY_COMPLETE
    表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的最边缘位置,系统会最优先考虑杀掉我们的应用程序,在这个时候应当尽可能地把一切可以释放的东西都进行释放。

2. OnTrimMemory回调中可以释放哪些资源?

通常在架构阶段就要考虑清楚,我们有哪些东西是要常驻内存的,有哪些是伴随界面存在的.一般情况下,有下面几种资源需要进行释放:

  • 缓存
    缓存包括一些文件缓存,图片缓存等,在用户正常使用的时候这些缓存很有作用,但当你的应用程序UI不可见的时候,这些缓存就可以被清除以减少内存的使用.比如第三方图片库的缓存.
  • 一些动态生成动态添加的View. 这些动态生成和添加的View且少数情况下才使用到的View,这时候可以被释放,下次使用的时候再进行动态生成即可.比如原生桌面中,会在OnTrimMemory的TRIM_MEMORY_MODERATE等级中,释放所有AppsCustomizePagedView的资源,来保证在低内存的时候,桌面不会轻易被杀掉.

1.1 onLowMemory& onTrimMemory优化,需要释放什么资源?

1. 哪些组件可以实现OnTrimMemory回调?

  • Application.onTrimMemory()
  • Activity.onTrimMemory()
  • Fragement.OnTrimMemory()
  • Service.onTrimMemory()
  • ContentProvider.OnTrimMemory()

2.1 例子:释放不常用到的View.

代码出处:Launcher

Launcher.java:

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
            mAppsCustomizeTabHost.onTrimMemory();
        }
    }

AppsCustomizeTabHost.java:

    public void onTrimMemory() {
        mContent.setVisibility(GONE);
        // Clear the widget pages of all their subviews - this will trigger the widget previews
        // to delete their bitmaps
        mPagedView.clearAllWidgetPages();
    }

AppsCustomizePagedView.java:

    public void clearAllWidgetPages() {
        cancelAllTasks();
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View v = getPageAt(i);
            if (v instanceof PagedViewGridLayout) {
                ((PagedViewGridLayout) v).removeAllViewsOnPage();
                mDirtyPageContent.set(i, true);
            }
        }
    }

PagedViewGridLayout.java

    @Override
    public void removeAllViewsOnPage() {
        removeAllViews();
        mOnLayoutListener = null;
        setLayerType(LAYER_TYPE_NONE, null);
    }

在内存紧张的时候,会回调OnLowMemory/OnTrimMemory,需要在回调方法中编写释放资源的代码。
可以在资源紧张的时候,释放UI 使用的资源资:Bitmap、数组、控件资源。

2. OnTrimMemory回调中可以释放哪些资源?

通常在架构阶段就要考虑清楚,我们有哪些东西是要常驻内存的,有哪些是伴随界面存在的.一般情况下,有下面几种资源需要进行释放:

  • 缓存
    缓存包括一些文件缓存,图片缓存等,在用户正常使用的时候这些缓存很有作用,但当你的应用程序UI不可见的时候,这些缓存就可以被清除以减少内存的使用.比如第三方图片库的缓存.
  • 一些动态生成动态添加的View.
    这些动态生成和添加的View且少数情况下才使用到的View,这时候可以被释放,下次使用的时候再进行动态生成即可.比如原生桌面中,会在
    OnTrimMemory的TRIM_MEMORY_MODERATE等级中,释放所有AppsCustomizePagedView的资源,来保证在低内存的时候,桌面不会轻易被杀掉.

2.2 例子: 清除缓存

代码出处:Contact

    @Override
    public void onTrimMemory(int level) {
        if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
            // Clear the caches.  Note all pending requests will be removed too.
            clear();
        }
    }

    public void clear() {
        mPendingRequests.clear();
        mBitmapHolderCache.evictAll();
        mBitmapCache.evictAll();
    }

1.2 OnLowMemory

2.1 例子:释放不常用到的View.

代码出处:Launcher

Launcher.java:

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
        mAppsCustomizeTabHost.onTrimMemory();
    }
}

AppsCustomizeTabHost.java:

public void onTrimMemory() {
    mContent.setVisibility(GONE);
    // Clear the widget pages of all their subviews - this will trigger the widget previews
    // to delete their bitmaps
    mPagedView.clearAllWidgetPages();
}

AppsCustomizePagedView.java:

public void clearAllWidgetPages() {
    cancelAllTasks();
    int count = getChildCount();
    for (int i = 0; i < count; i++) {
        View v = getPageAt(i);
        if (v instanceof PagedViewGridLayout) {
            ((PagedViewGridLayout) v).removeAllViewsOnPage();
            mDirtyPageContent.set(i, true);
        }
    }
}

PagedViewGridLayout.java

@Override
public void removeAllViewsOnPage() {
    removeAllViews();
    mOnLayoutListener = null;
    setLayerType(LAYER_TYPE_NONE, null);
}

3. OnTrimMemory和onStop的关系?

onTrimMemory()方法中的TRIM_MEMORY_UI_HIDDEN回调只有当我们程序中的所有UI组件全部不可见的时候才会触发,这和onStop()方法还是有很大区别的,因为onStop()方法只是当一个Activity完全不可见的时候就会调用,比如说用户打开了我们程序中的另一个Activity。

因此,我们可以在onStop()方法中去释放一些Activity相关的资源,比如说取消网络连接或者注销广播接收器等,但是像UI相关的资源应该一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)这个回调之后才去释放,这样可以保证如果用户只是从我们程序的一个Activity回到了另外一个Activity,界面相关的资源都不需要重新加载,从而提升响应速度。

需要注意的是,onTrimMemory的TRIM_MEMORY_UI_HIDDEN
等级是在onStop方法之前调用的.

OnLowMemory是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory。系统提供的回调有:
Application.onLowMemory()
Activity.OnLowMemory()
Fragement.OnLowMemory()
Service.OnLowMemory()
ContentProvider.OnLowMemory()

2.2 例子: 清除缓存

代码出处:Contact

@Override
public void onTrimMemory(int level) {
    if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
        // Clear the caches.  Note all pending requests will be removed too.
        clear();
    }
}

public void clear() {
    mPendingRequests.clear();
    mBitmapHolderCache.evictAll();
    mBitmapCache.evictAll();
}

4. OnTrimMemory和OnLowMemory的关系?

在引入OnTrimMemory之前都是使用OnLowMemory回调,需要知道的是,OnLowMemory大概和OnTrimMemory中的TRIM_MEMORY_COMPLETE级别相同,如果你想兼容api<14的机器,那么可以用OnLowMemory来实现,否则你可以忽略OnLowMemory,直接使用OnTrimMemory即可.

除了上述系统提供的API,还可以自己实现ComponentCallbacks,通过API注册,这样也能得到OnLowMemory回调。例如:

3. OnTrimMemory和onStop的关系?

onTrimMemory()方法中的TRIM_MEMORY_UI_HIDDEN回调只有当我们程序中的所有UI组件全部不可见的时候才会触发,这和onStop()方法还是有很大区别的,因为onStop()方法只是当一个Activity完全不可见的时候就会调用,比如说用户打开了我们程序中的另一个Activity。

因此,我们可以在onStop()方法中去释放一些Activity相关的资源,比如说取消网络连接或者注销广播接收器等,但是像UI相关的资源应该一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)这个回调之后才去释放,这样可以保证如果用户只是从我们程序的一个Activity回到了另外一个Activity,界面相关的资源都不需要重新加载,从而提升响应速度。

需要注意的是,onTrimMemory的TRIM_MEMORY_UI_HIDDEN
等级是在onStop方法之前调用的.

5. 为什么要调用OnTrimMemory?

尽管系统在内存不足的时候杀进程的顺序是按照LRU
Cache中从低到高来的,但是它同时也会考虑杀掉那些占用内存较高的应用来让系统更快地获得更多的内存。

所以如果你的应用占用内存较小,就可以增加不被杀掉的几率,从而快速地恢复(如果不被杀掉,启动的时候就是热启动,否则就是冷启动,其速度差在2~3倍)。

所以说在几个不同的OnTrimMemory回调中释放自己的UI资源,可以有效地提高用户体验。

 1 public static class MyCallback implements ComponentCallbacks {
 2  @Override
 3  public void onConfigurationChanged(Configuration arg) {
 4  }
 5 
 6  @Override
 7  public void onLowMemory() {
 8  //do release operation
 9  }
10  }

4. OnTrimMemory和OnLowMemory的关系?

在引入OnTrimMemory之前都是使用OnLowMemory回调,需要知道的是,OnLowMemory大概和
OnTrimMemory中的TRIM_MEMORY_COMPLETE级别相同,如果你想兼容api<14的机器,那么可以用
OnLowMemory来实现,否则你可以忽略OnLowMemory,直接使用OnTrimMemory即可.

6. 有哪些典型的使用场景?

然后,通过Context.registerComponentCallbacks
()在合适的时候注册回调就可以了。通过这种自定义的方法,可以在很多地方注册回调,而不需要局限于系统提供的组件。
onLowMemory
当后台程序已经终止资源还匮乏时会调用这个方法。好的应用程序一般会在这个方法里面释放一些不必要的资源来应付当后台程序已经终止,前台应用程序内存还不够时的情况。

5. 为什么要调用OnTrimMemory?

尽管系统在内存不足的时候杀进程的顺序是按照LRU
Cache中从低到高来的,但是它同时也会考虑杀掉那些占用内存较高的应用来让系统更快地获得更多的内存。

所以如果你的应用占用内存较小,就可以增加不被杀掉的几率,从而快速地恢复(如果不被杀掉,启动的时候就是热启动,否则就是冷启动,其速度差在2~3倍)。

所以说在几个不同的OnTrimMemory回调中释放自己的UI资源,可以有效地提高用户体验。

6.1 常驻内存的应用

一些常驻内存的应用,比如Launcher、安全中心、电话等,在用户使用过要退出的时候,需要调用OnTrimMemory来及时释放用户使用的时候所产生的多余的内存资源:比如动态生成的View、图片缓存、Fragment等。

1.3 OnTrimMemory

6. 有哪些典型的使用场景?

6.2 有后台Service运行的应用

这些应用不是常驻内存的,意味着可以被任务管理器杀掉,但是在某些场景下用户不会去杀。
这类应用包括:音乐、下载等。用户退出UI界面后,音乐还在继续播放,下载程序还在运行。这时候音乐应该释放部分UI资源和Cache(目前在播放音乐的时候,音乐应用占用将近140m内存)。

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()注册后,就会被系统回调到。

6.1 常驻内存的应用

一些常驻内存的应用,比如Launcher、安全中心、电话等,在用户使用过要退出的时候,需要调用OnTrimMemory来及时释放用户使用的时候所产生的多余的内存资源:比如动态生成的View、图片缓存、Fragment等。

1.4 OnLowMemory和OnTrimMemory的比较

6.2 有后台Service运行的应用

这些应用不是常驻内存的,意味着可以被任务管理器杀掉,但是在某些场景下用户不会去杀。这类应用包括:音乐、下载等。用户退出UI界面后,音乐还在继续播放,下载程序还在运行。这时候音乐应该释放部分UI资源和Cache。

1,OnLowMemory被回调时,已经没有后台进程;而onTrimMemory被回调时,还有后台进程。
2,OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory
killer
杀进程后触发;而OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。
3,通过一键清理后,OnLowMemory不会被触发,而OnTrimMemory会被触发一次。

使用举例:

1 @Override
2 public void onTrimMemory(int level) {
3 Log.e(TAG, " onTrimMemory ... level:" + level); 
4 }
5 
6 @Override
7 public void onLowMemory() { 
8 Log.e(TAG, " onLowMemory ... "); 
9 }

2.系统回调优化
2.1 回调原理:
在Application、
Activity、Fragement、Service、ContentProvider中都可以重写回调方法,对OnLowMemory/OnTrimMemory进行回调,在回调方法中实现资源释放的实现。
以Activity为例,在Activity源码中能够看到对于onTrimMemory的定义,因此在回调的时候重写方法即可。

1 public void onTrimMemory(int level) {
2 if (DEBUG_LIFECYCLE) Slog.v(TAG, "onTrimMemory " + this + ": " + level);
3   mCalled = true;
4   mFragments.dispatchTrimMemory(level);
5 }

2.2 释放资源:
在onTrimMemory释放资源,释放图片、数组、缓存等资源。

 1 @Override
 2 public void onTrimMemory(int level) {
 3 // TODO Auto-generated method stub
 4 DLog.d(" onTrimMemory ... level:" + level);
 5 
 6 switch(level)
 7 
 8 {
 9 case TRIM_MEMORY_UI_HIDDEN: 
10 //释放资源
11 /*编写释放资源代码*/
12 }
13 
14 break;
15 }
16 super.onTrimMemory(level);
17 }

下面是释放Bitmap的示例代码片段:

1 // 先判断是否已经回收
2 if(bitmap != null && !bitmap.isRecycled()){ 
3 // 回收并且置为null
4 bitmap.recycle(); 
5 bitmap = null; 
6 } 
7 System.gc();

list占用方法:
list.clear();然后在置空。

 

3.实现ComponentCallbacks

OnLowMemory除了上述系统提供的API,还可以自己实现ComponentCallbacks,通过API注册,这样也能得到OnLowMemory回调。例如:

 1 public static class ViewComponentCallbacks implements ComponentCallbacks {
 2 @Override
 3 public void onConfigurationChanged(Configuration arg) {
 4 }
 5 
 6 @Override
 7 public void onLowMemory() {
 8 //do release operation
 9 }
10 }

注册自定义的回调类:

1 ViewComponentCallbacks callBacks =new ViewComponentCallbacks();
2 this.registerComponentCallbacks( callBacks );

回调之后,即可进行重写:

1 @Override
2 public void onLowMemory() {
3 // TODO Auto-generated method stub
4 //释放资源的方法
5 super.onLowMemory();
6 }

 

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充分交换意见。

图片 1

当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

图片 2

可以看到系统为这个进程分配的PD是32452,那么 PPID是什么呐?

使用PS命令来看:adb shell ps | grep 379

图片 3

可以看到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:

图片 4

Foreground意味着此时你的进程处于前台进程,你的程序的界面可以被用户看到。此时我们使用adb
shell dumpsys meminfo com.preformance.liferecord来看这个进程的状态:

图片 5

可以看到Activity的数量为1(因为我只起了一个Activity)

当我们点击Back键的时候,我们可以使用命令adb shell dumpsys meminfo来查看
整个系统的状态,此时com.performance.liferecord这个进程状态为Cached:

图片 6

Cached状态标识你的应用进程变成了后台进程,位于LRU list里面。

此时我们使用adb shell dumpsys meminfo
com.performance.liferecord来看这个进程的状态:

图片 7

Activity的数量妥妥变成0了,说明没有内存泄漏。此时你的进程里面,是没有任何组件的。当然也不占用cpu的资源,但是会点内存(例子中我们可以看到占用了20706kb)的内存。

将后台进程放到Cached列表里而不是直接杀掉,其好处就是下一次这个进程的某个组件(最常见的就是Activity)启动的时候,不需要再创建新的进程
,速度杠杠的(比如就应用的热启动)。

onTrimMemory

此外当我们监听onTrimMemory回调方法的时候,假设我的缓存里最大的部分就是bitmap,请问就直接把bitmap从内存里抹去就可以了么?
那如果这么做,在进程重新被加载到前台来的时候,我怎么迅速恢复这些bitmap资源?

如答疑1所述:

对于
图片,一般采用三层缓存机制,即内存、文件、网络、释放内存后从文件中读取就好

一般的图片加载库都会提供内存/文件的缓存,即使内存中的Bitmap清除掉了,下一次从文件中读取也会很快的。

至少onTrimMemory的优点,比如你的应用Cache了20MB的Bitmap没有清除,当手机内存不足的时候,如果你没有及时释放
这些内存,那么 很可能你的进程
就被系统给杀掉了,这会你的Bitmap当然也就没了。用户下次启动你的应用的时候,我曹这货又被杀了,这手机系统有毛病吧?

是吧。。。做系统的还得背这个锅!

关于onTrimMemory的使用,可以参考博客

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图