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

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

相关文章
|
13天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
1月前
|
存储 缓存 监控
|
13天前
|
算法 JavaScript 前端开发
新生代和老生代内存划分的原理是什么?
【10月更文挑战第29天】新生代和老生代内存划分是JavaScript引擎为了更高效地管理内存、提高垃圾回收效率而采用的一种重要策略,它充分考虑了不同类型对象的生命周期和内存使用特点,通过不同的垃圾回收算法和晋升机制,实现了对内存的有效管理和优化。
|
1月前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
175 2
|
8天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
10天前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
18天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
128 9
|
21天前
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
49 8
|
22天前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
23天前
|
程序员 开发工具 Android开发
Android|WebView 禁止长按,限制非白名单域名的跳转层级
如何限制 WebView 仅域名白名单网址能随意跳转,并禁用长按选择文字。
28 2