Android 进程优先级 ADJ 算法:系统资源分配的 "城市管理法则"

170 阅读5分钟

一、城市管理法则:ADJ 的基本概念

在 Android 城市中,每个进程都是城市的 "居民",而 ADJ(Adjustment)则是居民的 "优先级标签"。系统作为 "城市管理员",会根据 ADJ 标签决定资源分配和进程清理顺序。

1.1 居民分类:ADJ 等级体系

ADJ 等级标签值居民类型待遇(存活优先级)
NATIVE_ADJ-1000原生系统进程最高优先级,几乎不被杀
SYSTEM_ADJ-900system_server 进程城市核心管理者,永不被杀
FOREGROUND_APP_ADJ0前台活跃应用用户正在使用,优先分配资源
VISIBLE_APP_ADJ100可见但非前台应用界面可见,较难被杀
PERCEPTIBLE_APP_ADJ200可感知进程(如后台音乐)用户可感知,中等优先级
SERVICE_ADJ500服务进程后台服务,默认优先级
CACHED_APP_ADJ900+缓存进程最低优先级,优先被清理

1.2 清理工 LMK:基于 ADJ 的资源回收

城市的 "清理工"LMK(Low Memory Killer)会根据 ADJ 标签清理进程:

  • 当内存不足时,优先清理 ADJ 值高的进程(如缓存进程)

  • ADJ≤0 的核心进程几乎不会被清理

  • 清理阈值随内存大小动态调整,64 位设备阈值更高

java

// LMK清理逻辑简化示意
void lmkCleanup() {
    // 内存阈值表(单位MB)
    int[] memoryThresholds = {73, 92, 110, 129, 221, 332};
    int[] adjLevels = {0, 100, 200, 300, 900, 906};
    
    int freeMemory = getFreeMemory();
    for (int i = memoryThresholds.length - 1; i >= 0; i--) {
        if (freeMemory < memoryThresholds[i]) {
            killProcessesWithAdj(adjLevels[i]); // 杀ADJ≥指定值的进程
            break;
        }
    }
}

二、居民待遇:各 ADJ 等级的 "生存法则"

2.1 顶级特权居民:ADJ≤0 的核心群体

SYSTEM_ADJ (-900):城市管理者 system_server

  • 城市的核心管理者,负责调度所有居民

  • 启动时通过AMS.setSystemProcess()设置 ADJ

  • 拥有最高权限,永不被杀

java

// system_server启动时设置ADJ
public void setSystemProcess() {
    ProcessRecord app = newProcessRecordLocked(...);
    app.persistent = true;
    app.maxAdj = ProcessList.SYSTEM_ADJ; // 设置为-900
    updateOomAdjLocked();
}

FOREGROUND_APP_ADJ (0):前台活跃居民

  • 用户正在交互的应用,如当前显示的 Activity

  • 触发条件:Activity 处于 resumed 状态、服务正在执行生命周期回调

java

// 前台进程判断逻辑
boolean isForeground(ProcessRecord app) {
    // 场景1:有resumed状态的Activity
    if (app.topActivity.state == ActivityState.RESUMED) {
        return true;
    }
    // 场景2:正在执行服务回调
    if (app.executingServices.size() > 0) {
        return true;
    }
    // 场景3:正在接收广播
    if (isReceivingBroadcastLocked(app)) {
        return true;
    }
    return false;
}

2.2 中级待遇居民:ADJ=100-500 的普通群体

VISIBLE_APP_ADJ (100):可见但非前台

  • Activity 可见但未处于前台(如被对话框部分遮挡)

  • Android P 引入更细粒度分级,根据 Task 层级调整 ADJ 值

java

// 可见进程ADJ计算
void calculateVisibleAdj(ProcessRecord app) {
    int minLayer = 99; // VISIBLE_APP_LAYER_MAX
    for (ActivityRecord activity : app.activities) {
        if (activity.visible) {
            // 根据Task层级调整ADJ
            TaskRecord task = activity.getTask();
            if (task != null) {
                app.curAdj = ProcessList.VISIBLE_APP_ADJ + task.mLayerRank;
            } else {
                app.curAdj = ProcessList.VISIBLE_APP_ADJ;
            }
            break;
        }
    }
}

SERVICE_ADJ (500):后台服务进程

  • 没有 UI 界面的服务进程,如后台数据同步

  • 30 分钟内活跃过的服务会维持较低 ADJ

java

// 服务进程ADJ判断
void calculateServiceAdj(ProcessRecord app) {
    for (ServiceRecord service : app.services) {
        if (service.startRequested) { // 服务正在运行
            long inactivityTime = System.currentTimeMillis() - service.lastActivity;
            if (inactivityTime < MAX_SERVICE_INACTIVITY) { // 30分钟内活跃过
                app.curAdj = ProcessList.SERVICE_ADJ;
                break;
            }
        }
    }
}

2.3 底层待遇居民:ADJ≥900 的缓存群体

CACHED_APP_ADJ (900+):缓存进程

  • 长时间未使用的进程,分为两类:

    • 有 Activity 的缓存进程(ADJ=900,902,904)
    • 空进程(ADJ=901,903,905)
  • 系统会限制缓存进程数量(默认 32 个),超过则清理

java

// 缓存进程清理逻辑
void cleanupCachedProcesses() {
    int maxCached = mConstants.CUR_MAX_CACHED_PROCESSES; // 默认32
    int numCached = getCachedProcessCount();
    
    if (numCached > maxCached) {
        // 按LRU顺序杀ADJ最高的缓存进程
        killProcessesByLruOrder(ProcessList.CACHED_APP_MIN_ADJ);
    }
}

三、城市管理策略:ADJ 与资源调度的关系

3.1 调度组:CPU 资源分配的 "优先级队列"

ADJ 决定进程的 CPU 调度组,类似居民的 "资源获取权限":

  • SCHED_GROUP_TOP_APP (2):前台进程,优先获取 CPU

  • SCHED_GROUP_DEFAULT (1):普通前台进程,中等资源

  • SCHED_GROUP_BACKGROUND (0):后台进程,最少资源

java

// 调度组设置逻辑
void setProcessSchedGroup(ProcessRecord app) {
    if (app.curAdj == ProcessList.FOREGROUND_APP_ADJ) {
        app.schedGroup = ProcessList.SCHED_GROUP_TOP_APP; // 最高调度组
    } else if (app.curAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
        app.schedGroup = ProcessList.SCHED_GROUP_DEFAULT; // 前台调度组
    } else {
        app.schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; // 后台调度组
    }
}

3.2 内存因子:动态调整的 "城市压力指数"

系统根据缓存进程数量计算 "内存因子",决定清理强度:

  • ADJ_MEM_FACTOR_CRITICAL (3):内存极度紧张,优先清理

  • ADJ_MEM_FACTOR_NORMAL (0):内存充足,较少清理

java

// 内存因子计算
int calculateMemoryFactor() {
    int numCached = getCachedProcessCount();
    int numEmpty = getEmptyProcessCount();
    int total = numCached + numEmpty;
    
    if (total <= TRIM_CRITICAL_THRESHOLD) { // ≤3
        return ADJ_MEM_FACTOR_CRITICAL;
    } else if (total <= TRIM_LOW_THRESHOLD) { // ≤5
        return ADJ_MEM_FACTOR_LOW;
    } else if (numCached <= 5 && numEmpty <= 8) {
        return ADJ_MEM_FACTOR_MODERATE;
    } else {
        return ADJ_MEM_FACTOR_NORMAL;
    }
}

四、开发者指南:如何优化应用的 "城市待遇"

4.1 进程分离:UI 与服务隔离

xml

<!-- 配置独立服务进程 -->
<service 
    android:name=".BackgroundService"
    android:process=":service" />
  • 避免 UI 进程与服务进程混合,防止 UI 进入后台时服务被连带清理
  • 独立服务进程 ADJ=500,比缓存进程 (900+) 更难被杀

4.2 前台服务:提升 ADJ 的 "通行证"

java

// 启动前台服务(如音乐播放)
public void startForegroundService() {
    Intent serviceIntent = new Intent(this, MusicService.class);
    startForegroundService(serviceIntent); // 提升ADJ到200
    
    // 在服务中设置通知
    Notification notification = new Notification.Builder(this)
            .setContentTitle("音乐播放")
            .setSmallIcon(R.drawable.ic_music)
            .build();
    startForeground(NOTIFICATION_ID, notification);
}
  • 前台服务 ADJ=PERCEPTIBLE_APP_ADJ (200),仅次于前台进程
  • 必须显示通知,否则会被系统惩罚

4.3 资源释放:遵守城市 "清理规则"

java

// 实现内存清理回调
@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (level >= TRIM_MEMORY_MODERATE) {
        // 释放非必要资源
        clearCaches();
    } else if (level >= TRIM_MEMORY_COMPLETE) {
        // 释放所有可重建资源
        releaseHeavyResources();
    }
}
  • 系统内存紧张时会回调onTrimMemory
  • 及时释放资源可降低进程被杀概率

五、城市管理总结:ADJ 算法的核心逻辑

  1. ADJ 决定存活优先级:值越低越难被杀,核心进程 ADJ≤0

  2. 动态调整机制:根据内存状态和进程活跃度动态计算 ADJ

  3. 调度组与 ADJ 关联:高 ADJ 进程获得更多 CPU 资源

  4. 开发者优化方向

    • 合理使用前台服务提升 ADJ

    • 分离 UI 与服务进程

    • 及时释放资源响应系统回调

通过理解 ADJ 算法,开发者可以让应用在 Android 城市中获得更优的 "居住待遇",同时避免过度占用资源,维持城市的整体稳定。