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和电量。在使用白名单低内存过滤,需要考虑到应用的实际需求 尽量避免对系统和其他应用造成负面影响。

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

相关文章
|
2天前
|
缓存 算法 安全
深入理解操作系统内存管理:分页系统的优势与挑战
【5月更文挑战第31天】 在现代操作系统中,内存管理是核心功能之一。分页系统作为内存管理的一种流行技术,其设计哲学基于时间和空间的局部性原理,旨在提高内存利用率和系统性能。本文将探讨分页系统的关键优势及其面临的挑战,包括页面置换算法、内存碎片问题以及虚拟到物理地址转换的复杂性。通过对分页机制的深入分析,我们揭示了它在多任务处理环境中如何允许多个进程共享主存资源,并保证了操作系统的稳定性与高效性。
|
3天前
|
程序员 内存技术
深入理解操作系统内存管理:原理与实践
【5月更文挑战第30天】 在现代计算机系统中,操作系统的内存管理是确保系统高效、稳定运行的关键。本文将深入探讨操作系统中内存管理的理论基础和实际应用,包括物理内存与虚拟内存的映射机制、分页与分段技术、以及内存分配策略等。通过对内存管理机制的分析与案例实践,旨在为读者提供一个清晰的内存管理概念框架和操作指南,帮助理解操作系统如何优化内存资源使用,提高系统性能。
|
3天前
|
存储 Linux 开发者
深入理解操作系统内存管理:原理与实践
【5月更文挑战第30天】 本文旨在探讨操作系统中的内存管理机制,其核心是内存的有效分配、使用与回收。我们将从内存管理的基本原理出发,逐步深入到具体的实现技术,如分页、分段和虚拟内存等。文章将详细阐述每种技术的工作原理、优势及其可能面临的问题,并通过实际案例来展示这些技术在现代操作系统中的应用。通过阅读本文,读者将对操作系统的内存管理有一个全面而深入的了解,并能够对常见的内存问题进行诊断和优化。
|
3天前
|
缓存 算法 Java
深入理解操作系统内存管理:原理与实践
【5月更文挑战第30天】 操作系统的心脏——内存管理,是确保系统高效稳定运行的关键。本文将深入剖析操作系统内存管理的基本原理,包括物理内存与虚拟内存的映射机制、分页与分段技术、以及内存分配策略等。同时,结合现代操作系统实例,探讨内存管理在多任务环境中的创新应用,如Linux内核的内存管理优化。文章旨在为读者提供一个全面、深入的视角,以理解并掌握操作系统中这一至关重要的组成部分。
|
3天前
|
存储 缓存 算法
深入理解操作系统的内存管理:原理与实践
【5月更文挑战第30天】 在现代计算机系统中,操作系统扮演着至关重要的角色,尤其是内存管理作为其核心功能之一。本文将详细探讨操作系统内存管理的基本原理、关键技术以及实际应用场景。我们将从内存层次结构出发,解析地址转换和分页机制,并探讨虚拟内存技术如何使得系统运行更加高效稳定。同时,通过分析具体案例,本文还将展示内存管理在提升系统性能和安全性方面的重要作用。
|
4天前
|
运维 监控 Android开发
构建高效自动化运维系统的策略与实践构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第29天】随着信息技术的迅猛发展,企业IT基础设施变得日益复杂,传统的手动运维模式已难以满足高效率、高稳定性的要求。本文将深入探讨如何通过自动化工具和策略来构建一个高效的自动化运维系统。文中不仅分析了自动化运维的必要性,还详细介绍了实现过程中的关键步骤,包括监控、配置管理、故障响应等,并结合实际案例分析其效果,以期为读者提供一套行之有效的自动化运维解决方案。
|
4天前
|
算法 安全 调度
深入理解操作系统内存管理:原理与实践
【5月更文挑战第29天】 在现代计算机系统中,操作系统扮演着至关重要的角色,其中内存管理是其核心职能之一。本文旨在剖析操作系统中内存管理的基本原理和关键技术,以及它们如何在不同类型的操作系统中得以实现。我们将从内存的分配与回收机制入手,探讨分页、分段以及虚拟内存等概念,并分析它们如何共同作用以支持多任务处理和保护系统安全。通过实例演示和性能分析,本文为读者呈现一个全面而深入的操作系统内存管理视角。
|
4天前
|
Java API
DirectByteBuffer内存释放原理
DirectByteBuffer内存释放原理
10 0
|
4天前
|
人工智能 vr&ar Android开发
安卓与iOS系统的发展趋势及影响分析
在移动互联网时代,安卓和iOS作为两大主流移动操作系统,在不断发展变化中展现出不同的特点和发展趋势。本文从技术性角度出发,分析了安卓和iOS系统的发展趋势,并探讨了它们对移动设备市场和用户体验的影响,帮助读者更好地理解当前移动操作系统的发展方向和未来可能的变化。
8 0
|
4天前
|
存储 Java 编译器
Java方法的基本内存原理与代码实例
Java方法的基本内存原理与代码实例
12 0