背景知识
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。
- 通过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的大小、已分配和未使用的内存。如果已分配的内存接近总大小,并且可用的内存很少,那么应用可能很快就会耗尽内存。
- 内存映射: 如果应用加载了大量的库或资源,那么这些内存映射可能会很大。这可能是一个优化的地方,特别是如果应用加载了不需要的库或资源。
- 对象数量: 如果某些对象的数量异常地高,那么可能存在内存泄漏。
- 通过adb命令获取某个应用的OOM_ADJ值:
在Android系统中,OOM_ADJ值被用来决定当系统内存不足时,哪些进程应该被杀死。你可以使用以下的adb shell命令来获取某个应用的OOM_ADJ值:
- 使用
ps -A | grep com.xxx.r58_test
找到了应用的进程ID,即3307
。 - 使用
cat /proc/3307/oom_score_adj
命令查看了该进程的oom_score_adj
值,其值为0
。
这意味着该应用的oom_score_adj
值为200,表示该应用是一个后台进程。这个值越高,进程被杀死的可能性就越大;这个值越低,进程被杀死的可能性就越小。
要手动修改一个进程的 oom_score_adj
值,需要具有root权限。如果设备已经root,可以按照以下步骤操作:
- 获取root权限:
adb shell su
- 修改
oom_score_adj
值:
你可以使用echo
命令将新的值写入oom_score_adj
文件。例如,要将com.btf.r58_test
的值设置为900
,可以执行:
echo 900 > /proc/3307/oom_score_adj
- 验证修改:
再次使用cat
命令来确认oom_score_adj
的值已经被修改:
cat /proc/3307/oom_score_adj
修改oom_score_adj
值可能会导致进程在内存紧张时更容易被系统杀死。
如果设备没有root权限,那么无法修改oom_score_adj
值。在这种情况下,可能需要考虑其他方法来模拟内存压力或测试应用的行为。
- 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和电量。在使用白名单低内存过滤,需要考虑到应用的实际需求 尽量避免对系统和其他应用造成负面影响。
希望这篇文章能对您有所帮助。如果您还有其他问题或建议,请留言与私信。