Android系统 实现低内存白名单防LMK原理分析

简介: Android系统 实现低内存白名单防LMK原理分析

背景知识

Android系统为了保证系统流畅性和稳定性,在内存不足时会采取一些措施来释放内存,比如杀死一些后台进程。系统会根据每个进程的重要性和占用内存大小来决定杀死哪些进程,这个过程称为低内存杀死(Low Memory Killer,LMK)。系统会为每个进程分配一个OOM_ADJ值,表示该进程的重要性,值越低表示越重要,越不容易被杀死。系统还会定义一组OOM_MINFREE值,表示不同重要性的进程在内存不足时的最小保留内存。当系统可用内存低于某个OOM_MINFREE值时,系统就会杀死所有OOM_ADJ值高于或等于该值的进程。

但是,有些情况下,我们可能需要让某些进程不受LMK的影响,即使它们的OOM_ADJ值很高,也不会被杀死。这时候,我们就可以使用白名单低内存过滤来指定哪些进程可以绕过LMK的检查,而不需要修改它们的OOM_ADJ值。白名单低内存过滤是一个字符串数组,存储在frameworks/base/core/res/res/values/config.xml文件中,名为low_memory_killer_tracker_whitelist。我们可以在这个数组中添加我们想要保护的进程的包名,例如com.xxx.r58_test。

  1. 通过adb命令获取某个应用占用的内存:
    你可以使用以下的adb shell命令来获取某个应用占用的内存信息:
130|rk3568_r:/ # dumpsys meminfo package_name
Applications Memory Usage (in Kilobytes):
Uptime: 1385734 Realtime: 1385734
** MEMINFO in pid 3307 [package_name] **
                   Pss  Private  Private     Swap      Rss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty    Total     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------   ------
  Native Heap    20510    20428        0        0    23216    29372    19336     6423
  Dalvik Heap     2189     2036        0        0     6516     4664     2333     2331
 Dalvik Other     1346      744        0        0     2540
        Stack      688      688        0        0      696
       Ashmem        2        0        0        0        8
    Other dev       20        0       20        0      252
     .so mmap     4803      208        0        0    55596
    .jar mmap     1725        0        0        0    27404
    .apk mmap      931        0        0        0    25092
    .dex mmap     2975     2964        0        0     3076
    .oat mmap       73        0        0        0     2164
    .art mmap     7812     7368        4        0    20052
   Other mmap       62       32        0        0     1028
      Unknown      480      456        0        0     1160
        TOTAL    43616    34924       24        0    43616    34036    21669     8754
 App Summary
                       Pss(KB)                        Rss(KB)
                        ------                         ------
           Java Heap:     9408                          26568
         Native Heap:    20428                          23216
                Code:     3172                         114612
               Stack:      688                            696
            Graphics:        0                              0
       Private Other:     1252
              System:     8668
             Unknown:                                    3708
           TOTAL PSS:    43616            TOTAL RSS:   168800      TOTAL SWAP (KB):        0
 Objects
               Views:        8         ViewRootImpl:        1
         AppContexts:        7           Activities:        2
              Assets:        6        AssetManagers:        0
       Local Binders:       11        Proxy Binders:       32
       Parcel memory:        6         Parcel count:       24
    Death Recipients:        0      OpenSSL Sockets:        0
            WebViews:        0
 SQL
         MEMORY_USED:        0
  PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0

其中,<package_name>是你想要查询的应用的包名。这个命令会返回一份详细的内存使用报告,包括PSS,Private Dirty,Private Clean等内存使用情况。

dumpsys meminfo输出,应用内存使用和其他重要值的详细表格:

项目 简介
应用包名 package_name 应用的包名
总PSS 43616 KB 估计的应用实际使用的内存量
总RSS 168800 KB 应用在物理内存中的实际占用
Java堆 Java对象存储的地方
- PSS 9408 KB 估计的Java堆实际使用的内存量
- RSS 26568 KB Java堆在物理内存中的实际占用
Native堆 C和C++代码分配的内存
- PSS 20428 KB 估计的Native堆实际使用的内存量
- RSS 23216 KB Native堆在物理内存中的实际占用
- 已分配 19336 KB 已分配的Native堆内存
- 未使用 6423 KB 未使用的Native堆内存
代码 应用代码的内存
- PSS 3172 KB 估计的代码实际使用的内存量
- RSS 114612 KB 代码在物理内存中的实际占用
堆栈 应用线程的堆栈内存
- PSS 688 KB 估计的堆栈实际使用的内存量
- RSS 696 KB 堆栈在物理内存中的实际占用
系统 系统使用的内存
- PSS 8668 KB 估计的系统实际使用的内存量
对象 应用创建的对象数量
- Views 8 应用中的视图数量
- Activities 2 应用中的活动数量
SQL 应用使用的SQLite数据库的内存信息
- MEMORY_USED 0 KB 使用的数据库内存

根据我查阅的资料 , 可以关注总PSS值、Native堆的已分配和未使用内存,这些通常是最关键的指标。

分析建议:

  • 关注PSS: PSS是一个很好的指标,它考虑了共享内存。如果PSS值很高,那么应用可能正在使用大量内存。
  • Java和Native Heap: 查看Java和Native Heap的大小、已分配和未使用的内存。如果已分配的内存接近总大小,并且可用的内存很少,那么应用可能很快就会耗尽内存。
  • 内存映射: 如果应用加载了大量的库或资源,那么这些内存映射可能会很大。这可能是一个优化的地方,特别是如果应用加载了不需要的库或资源。
  • 对象数量: 如果某些对象的数量异常地高,那么可能存在内存泄漏。
  1. 通过adb命令获取某个应用的OOM_ADJ值:

在Android系统中,OOM_ADJ值被用来决定当系统内存不足时,哪些进程应该被杀死。你可以使用以下的adb shell命令来获取某个应用的OOM_ADJ值:

  1. 使用ps -A | grep com.xxx.r58_test找到了应用的进程ID,即3307
  2. 使用cat /proc/3307/oom_score_adj命令查看了该进程的oom_score_adj值,其值为0

这意味着该应用的oom_score_adj值为200,表示该应用是一个后台进程。这个值越高,进程被杀死的可能性就越大;这个值越低,进程被杀死的可能性就越小。

要手动修改一个进程的 oom_score_adj 值,需要具有root权限。如果设备已经root,可以按照以下步骤操作:

  1. 获取root权限:
adb shell su
  1. 修改oom_score_adj:
    你可以使用echo命令将新的值写入oom_score_adj文件。例如,要将com.btf.r58_test的值设置为900,可以执行:
echo 900 > /proc/3307/oom_score_adj
  1. 验证修改:
    再次使用cat命令来确认oom_score_adj的值已经被修改:
cat /proc/3307/oom_score_adj

修改oom_score_adj值可能会导致进程在内存紧张时更容易被系统杀死。

如果设备没有root权限,那么无法修改oom_score_adj值。在这种情况下,可能需要考虑其他方法来模拟内存压力或测试应用的行为。

  1. OOM_ADJ值的范围:

在Android系统中,OOM_ADJ的值范围是从-1000(永远不会被杀死)到1000(总是被优先杀死)。这个值越高,进程被杀死的可能性就越大。以下是OOM_ADJ值的详细表格:

OOM_ADJ值 描述 备注
-1000 系统进程 这些进程是系统的一部分,不会被杀死。
-900 前台应用 用户正在与这些应用交互,或者这些应用正在执行某些用户期望的操作(例如播放音乐)。这些应用最后被杀死。
-800 可见应用 这些应用对用户可见,但用户并未与它们交互。例如,它们可能在屏幕上显示一个对话框,但用户正在与另一个应用交互。
-700 次要服务 这些应用正在运行一些用户可能注意到的服务。例如,正在下载或上传文件。
-600 后台进程 用户已经停止与这些应用交互,但它们仍然在运行一些服务。这些应用在内存不足时会被优先杀死。
-500 内容提供者 这些应用提供了其他应用可能正在使用的内容。
-400 空服务 这些应用没有运行任何代码,但系统仍然保留它们,以便在需要时快速启动。
0至1000 缓存进程 这些应用没有运行任何代码,也没有被任何活动的应用引用。它们只是在内存中,以便在需要时快速启动。这些应用在内存不足时会首先被杀死。

修改方法

要增加白名单低内存过滤,我们只需要在low_memory_killer_tracker_whitelist数组中添加我们想要保护的进程的包名即可。例如,如果我们想要让com.xxx.r58_test这个应用不受LMK的影响,那么我们可以在数组中添加com.xxx.r58_test这个字符串。具体的修改代码如下:

--- a/frameworks/base/core/res/res/values/config.xml
+++ b/frameworks/base/core/res/res/values/config.xml
@@ -4403,4 +4403,10 @@
 
     <!-- Component names of the services which will keep critical code path warm -->
     <string-array name="config_keep_warming_services" translatable="false" />
+    <string-array translatable="false" name="low_memory_killer_tracker_whitelist" >
+  <item>"com.xxx.r58_test"</item>
+ </string-array>
 </resources>
diff --git a/frameworks/base/core/res/res/values/symbols.xml b/frameworks/base/core/res/res/values/symbols.xml
index 3ef0a8dc9d..fc86ead8b6 100644
--- a/frameworks/base/core/res/res/values/symbols.xml
+++ b/frameworks/base/core/res/res/values/symbols.xml
@@ -22,6 +22,8 @@
        Can be referenced in java code as: com.android.internal.R.<type>.<name>
        and in layout xml as: "@*android:<type>/<name>"
   -->
+   <java-symbol type="array" name="low_memory_killer_tracker_whitelist" />
   <java-symbol type="id" name="account_name" />
   <java-symbol type="id" name="account_row_icon" />
   <java-symbol type="id" name="account_row_text" />

但是,这样修改只是改变了白名单低内存过滤中的内容,并没有改变LMK的实际行为。也就是说,即使我们在白名单中添加了com.xxx.r58_test这个字符串,LMK还是会根据该应用的OOM_ADJ值和OOM_MINFREE值来判断是否要杀死它。因此,我们还需要在OomAdjuster.java文件中修改LMK的逻辑,让它在检查每个进程是否需要被杀死时,先判断该进程是否在白名单中。如果是,则跳过该进程,不进行后续的检查和操作;如果否,则按照原来的逻辑继续执行。需要在applyOomAdjLocked方法中添加一个isInWhitelist方法来判断一个进程是否在白名单中,并在设置每个进程的oom_adj值时调用该方法。具体的修改代码如下:

diff --git a/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java b/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
index faa7dce316..4fc8a4698f 100644
--- a/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.content.res.Resources;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
@@ -71,6 +72,7 @@ import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerService.TOP_APP_PRIORITY_BOOST;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 
+import java.util.List;
 import android.app.ActivityManager;
 import android.app.ApplicationExitInfo;
 import android.app.usage.UsageEvents;
@@ -2144,6 +2146,25 @@ public final class OomAdjuster {
         }
     }
 
+
+private List<String> mLmKillerBypassPackages = new ArrayList<>();
+
+private String[] lmKillerTrackerWhitelist = Resources.getSystem().getStringArray(
+    com.android.internal.R.array.low_memory_killer_tracker_whitelist);
+private List<String> lmKillerBypassPackages = Arrays.asList(lmKillerTrackerWhitelist);
+
+    private boolean isInWhitelist(ProcessRecord pr) {
+        String pkgName = pr.info.packageName;
+    
+        for (String token : mLmKillerBypassPackages) { 
+            if (pkgName.startsWith(token)) {
+                return true; 
+            }
+        }
+        return false;
+    }
+
+
     /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
     @GuardedBy("mService")
     private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
@@ -2163,6 +2184,30 @@ public final class OomAdjuster {
                 // Perform a minor compaction when a perceptible app becomes the prev/home app
                 // Perform a major compaction when any app enters cached
                 // reminder: here, setAdj is previous state, curAdj is upcoming state
+           boolean isAppWhiteProcess = false;
+            if( isInWhitelist(app) && (app.curAdj > ProcessList.PERSISTENT_SERVICE_ADJ))
+                isAppWhiteProcess = true;
+            if(isAppWhiteProcess){
+                Slog.d(TAG,"isAppWhiteProcess so not kill!");
+               ProcessList.setOomAdj(app.pid, app.uid, ProcessList.PERSISTENT_SERVICE_ADJ);
+        if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
+                    String msg = "Set " + app.pid + " " + app.processName + " adj "
+                            + app.curAdj + ": " + app.adjType;
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+                }    
+                app.setAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+                app.verifiedAdj = ProcessList.INVALID_ADJ;
+            }else{
+                ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
+                if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
+                    String msg = "Set " + app.pid + " " + app.processName + " adj "
+                            + app.curAdj + ": " + app.adjType;
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+                }
+                app.setAdj = app.curAdj;
+                app.verifiedAdj = ProcessList.INVALID_ADJ;
+ }
+
                 if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
                         (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
                                 app.curAdj == ProcessList.HOME_APP_ADJ)) {

修改效果

修改后,com.xxx.r58_test这个应用不会被LMK杀死,即使它的OOM_ADJ值很高,也不会受到OOM_MINFREE值的限制。这样可以保证该应用的后台服务和功能正常运行,比如接收消息、执行任务等。这也可能带来一些性能问题和资源浪费,比如该应用占用过多内存导致其他应用被杀死或无法启动,或者该应用后台执行一些无用或恶意的操作消耗CPU和电量。在使用白名单低内存过滤,需要考虑到应用的实际需求 尽量避免对系统和其他应用造成负面影响。

希望这篇文章能对您有所帮助。如果您还有其他问题或建议,请留言与私信。

相关文章
|
1月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
160 4
|
1月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
21天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
24天前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
29 8
|
22天前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
28天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
28天前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
34 1
|
存储 编解码 Android开发
Android内存优化-Bitmap内存优化
在日常开发中,我们不免会使用到Bitmap,而bitmap确实实在在的是内存使用的 “大户”,如何更好的使用 bitmap,减少其对 App内存的使用,是我们开发中不可回避的问题。
190 0
Android内存优化-Bitmap内存优化
|
存储 编解码 缓存