버전 기반: Android S
0. 서문
Android Q에는 프레임워크 측 앱 메모리 재활용 최적화 솔루션이 추가되었습니다. 앱의 oom adj가 구체적으로 변경되면 프레임워크 측에서 애플리케이션의 메모리를 처리합니다. 버전이 발전하면서 이 부분의 최적화 작업이 개선되었는데, 저자는 이 부분의 최적화 과정을 안드로이드 R 과 안드로이드 S 각각에 대해 자세히 분석해 보겠습니다.
이전 글 에서는 안드로이드 R 버전 에 대한 상세한 분석을 진행하였고 , 이번 글에서는 계속해서 안드로이드 S 버전에 대한 분석을 진행한다.
알아채다:
본 글에서 언급된 "압축"이라는 단어는 실제로 메모리 재활용 최적화를 의미하는 것으로, 정확한 로직이 밝혀져야만 익명 페이지 재활용인지 파일 페이지 재활용인지 명확해지기 때문입니다. 그 전에는 컴팩트를 사용하기로 잠정 결정했습니다. 처리.
또한, 본 글의 분석 내용을 안드로이드 R 버전과 비교하고, 마지막으로 두 버전 간의 차이점과 장점, 단점을 정리한다.
1. CachedAppOptimizer 클래스
앱 측의 메모리 압축 관리는 CachedAppOptimizer 클래스에서 완료됩니다.
이전 블로그 게시물 "oom_adj 업데이트 원리 (1)" 및 "oom_adj 업데이트 원리 (2)" 에서 AMS가 OomAdjuster 클래스를 통해 oom_adj의 업데이트, 계산 및 적용을 관리한다는 것을 배웠습니다. CachedAppOptimizer 개체는 OomAdjuster에서도 인스턴스화됩니다.
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
ServiceThread adjusterThread) {
mService = service;
...
mCachedAppOptimizer = new CachedAppOptimizer(mService);
...
}
매개변수는 AMS 개체입니다.
CachedAppOptimizer 구조를 살펴보겠습니다 .
frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java
public CachedAppOptimizer(ActivityManagerService am) {
this(am, null, new DefaultProcessDependencies());
}
@VisibleForTesting
CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,
ProcessDependencies processDependencies) {
mAm = am;
mProcLock = am.mProcLock;
mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
mCompactionPriority, true);
mProcStateThrottle = new HashSet<>();
mProcessDependencies = processDependencies;
mTestCallback = callback;
mSettingsObserver = new SettingsContentObserver();
mProcLocksReader = new ProcLocksReader();
}
ServiceThread는 이름이 CachedAppOptimizerThread이고 우선순위가 THREAD_GROUP_SYSTEM 인 구성에서 생성됩니다 .
mProcessDependency는 최종 압축 처리에 사용되는 DefaultProcessDependency 유형의 개체입니다.
안드로이드 R과의 차이점 :
- AMS의 프로세스 관리를 동기화하기 위해 새로운 mProcLock 변수가 추가되었습니다 .
- 캐시된_apps_freezer의 변경 사항을 모니터링하기 위해 새로운 mSettingsObserver 변수가 추가되었습니다 .
- 프로세스가 정지되었을 때 /proc/locks 정보를 볼 수 있도록 새로운 mProcLocksReader 변수가 추가되었습니다 .
2. 초기화()
Android R 버전과 달리 Android R의 init() 함수는 SystemServer .startOtherServices () 및 mOomAdjuster .initSettings () 가 installSystemProviders()에서 호출될 때 AMS에서installSystemProviders() 함수를 호출하여 트리거됩니다.
Android S에서는 installSystemProviders()가 ContentProviderHelper .java 에 정의되어 있습니다 .
CachedAppOptimizer .init ()는 OomAdjuster .initSettings () 에서 호출됩니다 .
frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java
public void init() {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
ActivityThread.currentApplication().getMainExecutor(),
mOnNativeBootFlagsChangedListener);
mAm.mContext.getContentResolver().registerContentObserver(
CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);
synchronized (mPhenotypeFlagLock) {
updateUseCompaction();
updateCompactionActions();
updateCompactionThrottles();
updateCompactStatsdSampleRate();
updateFreezerStatsdSampleRate();
updateFullRssThrottle();
updateFullDeltaRssThrottle();
updateProcStateThrottle();
updateUseFreezer();
updateMinOomAdjThrottle();
updateMaxOomAdjThrottle();
}
}
Android R과 비교하면 고정된 속성에 대한 추가 리스너인 mOnNativeBootFlagsChangedListener가 있습니다.
게다가 마지막에 두 가지 업데이트 기능이 더 있습니다.
2.1 updateUseCompaction()
private void updateUseCompaction() {
mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
mCompactionPriority = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACTION_PRIORITY, Process.THREAD_GROUP_BACKGROUND);
if (mUseCompaction && mCompactionHandler == null) {
if (!mCachedAppOptimizerThread.isAlive()) {
mCachedAppOptimizerThread.start();
}
mCompactionHandler = new MemCompactionHandler();
}
Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
mCompactionPriority);
}
Android R과 비교하여 여기에서는 스레드 우선순위가 동적으로 구성되며 DeviceConfig의 Compaction_priority 속성을 통해 우선순위를 설정할 수 있습니다. 또한 우선순위 설정은 mUsecompaction 활성화 여부에 따라 달라지지 않습니다.
다른 모든 것은 R과 동일합니다. 먼저 use_compaction 속성을 가져옵니다. 기본값은 DEFAULT_USE_COMPACTION( false )입니다.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
user_compation이 활성화되면 비동기 메시지 처리를 위해 mCompactionHandler가 생성되고 생성자에서 생성된 ServiceThread가 시작됩니다.
MemCompactionHandler 클래스를 살펴보겠습니다.
private final class MemCompactionHandler extends Handler {
private MemCompactionHandler() {
super(mCachedAppOptimizerThread.getLooper());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COMPACT_PROCESS_MSG: {
...
break;
}
case COMPACT_SYSTEM_MSG: {
...
break;
}
}
}
Looper는 주로 압축된 프로세스 메모리나 시스템 프로세스 메모리를 처리하는데 사용되는 ServiceThread의 Looper를 사용합니다.
2.2 updateCompactionActions()
private void updateCompactionActions() {
int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
mCompactActionSome = compactActionIntToString(compactAction1);
mCompactActionFull = compactActionIntToString(compactAction2);
}
안드로이드 R과 동일합니다
2.3 updateCompactionThrottles()
private void updateCompactionThrottles() {
boolean useThrottleDefaults = false;
// TODO: improve efficiency by calling DeviceConfig only once for all flags.
String throttleSomeSomeFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_1);
String throttleSomeFullFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_2);
String throttleFullSomeFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_3);
String throttleFullFullFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_4);
String throttleBFGSFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_5);
String throttlePersistentFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_6);
String throttleMinOomAdjFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_MIN_OOM_ADJ);
String throttleMaxOomAdjFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_MAX_OOM_ADJ);
if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
|| TextUtils.isEmpty(throttleFullSomeFlag)
|| TextUtils.isEmpty(throttleFullFullFlag)
|| TextUtils.isEmpty(throttleBFGSFlag)
|| TextUtils.isEmpty(throttlePersistentFlag)
|| TextUtils.isEmpty(throttleMinOomAdjFlag)
|| TextUtils.isEmpty(throttleMaxOomAdjFlag)) {
// Set defaults for all if any are not set.
useThrottleDefaults = true;
} else {
try {
mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag);
mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag);
mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag);
} catch (NumberFormatException e) {
useThrottleDefaults = true;
}
}
if (useThrottleDefaults) {
mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
}
}
Android R과 비교하면 adj에 대한 제한 값이 두 개 더 있습니다. mCompactThrottleMinOomAdj와 mCompactThrottleMaxOomAdj입니다. FULL로 압축하는 경우 마지막 oom_adj가 캐시되지 않아야 합니다. R 버전에서는 이러한 조건부 판단이 OomAdjuster.applyOomAdjLocked() 함수에 배치되지만, S 버전에서는 여기에 배치됩니다. 저자는 이 디자인의 원래 의도가 사용자가 FULL 압축된 조정을 제어할 수 있도록 하는 것이라고 추측하지만, applyOomAdjLSP() 함수와 비교하면 Android R이 더 좋습니다.
2.4 업데이트FullRssThrottle()
private void updateFullRssThrottle() {
mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
// Don't allow negative values. 0 means don't apply the throttle.
if (mFullAnonRssThrottleKb < 0) {
mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
}
}
2.5 업데이트FullDeltaRssThrottle()
private void updateFullDeltaRssThrottle() {
mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
if (mFullDeltaRssThrottleKb < 0) {
mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
}
}
2.6 업데이트ProcStateThrottle()
private void updateProcStateThrottle() {
String procStateThrottleString = DeviceConfig.getString(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE,
DEFAULT_COMPACT_PROC_STATE_THROTTLE);
if (!parseProcStateThrottle(procStateThrottleString)) {
Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \""
+ procStateThrottleString + "\" falling back to default.");
if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) {
Slog.wtf(TAG_AM,
"Unable to parse default app compact proc state throttle "
+ DEFAULT_COMPACT_PROC_STATE_THROTTLE);
}
}
}
2.7 updateUseFreezer()
프로세스 동결 상태가 초기화되며 동결 최적화는 나중에 별도로 분석됩니다.
2.8 업데이트MinOomAdjThrottle()
private void updateMinOomAdjThrottle() {
mCompactThrottleMinOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
// Should only compact cached processes.
if (mCompactThrottleMinOomAdj < ProcessList.CACHED_APP_MIN_ADJ) {
mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
}
}
private void updateMaxOomAdjThrottle() {
mCompactThrottleMaxOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
// Should only compact cached processes.
if (mCompactThrottleMaxOomAdj > ProcessList.CACHED_APP_MAX_ADJ) {
mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
}
}
이 두 가지 기능은 Android S에 추가되었으며 주로 FULL 압축을 판단하는 데 사용됩니다.
3. 압축 최적화 개시자
압축 최적화는 다양한 상황에서 실행됩니다.CachedAppOptimizer에서 압축 최적화 개시자는 주로 다음을 포함합니다.
- 컴팩트앱썸()
- 컴팩트앱전체()
- 컴팩트앱지속성()
- 컴팩트앱Bfgs()
- 컴팩트올시스템()
중요한 로직은 Android R 버전과 유사하며, 이 글에서는 처음 두 가지 기능을 주로 분석합니다.
3.1 컴팩트앱썸()
OomAdjuster .applyOomAdjLSP() 에서 트리거됨 :
private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
long nowElapsed) {
...
if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
// Cached and prev/home compaction
if (state.getCurAdj() != state.getSetAdj()) {
// 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
if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ
&& (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ
|| state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {
mCachedAppOptimizer.compactAppSome(app);
} else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
&& state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {
mCachedAppOptimizer.compactAppFull(app);
}
} else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
&& state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
// processing of the requests. As a result, there is throttling both here
// and in CachedAppOptimizer.
&& mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
mCachedAppOptimizer.compactAppPersistent(app);
} else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
&& state.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
mCachedAppOptimizer.compactAppBfgs(app);
}
}
...
}
Andoird R 버전과 비교하면 여기서는 FULL 압축의 로직이 조정되었으며, R에서 Non-cached 에서 Cached 로 판단하는 것은 현재 캐시된 상태에만 관심을 갖고 CompactAppFull()에서 확인하도록 변경되었습니다. 저자는 이것이 단순히 조건을 완전히 CompactAppFull()에 맡긴다고 생각하는데, 글쎄요, 디자인의 목적은 모르겠습니다.
여기서는 논리적 분석을 생략하였으며, 자세한 내용은 안드로이드 R 버전에서 확인하실 수 있습니다 .
void compactAppSome(ProcessRecord app) {
app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
if (!app.mOptRecord.hasPendingCompact()) {
app.mOptRecord.setHasPendingCompact(true);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
}
}
Android R과 비교하여 ProcessRecord 클래스에는 mOptRecord 유형의 추가 멤버 변수가 있습니다.
ProcessCachedOptimizerRecord는 CachedAppOptimizer 클래스와 협력하도록 설계되었습니다.
그 외 로직은 Android R과 동일합니다.
3.2 컴팩트앱풀()
void compactAppFull(ProcessRecord app) {
// Apply OOM adj score throttle for Full App Compaction.
if ((app.mState.getSetAdj() < mCompactThrottleMinOomAdj
|| app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
&& app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
&& app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj) {
app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
if (!app.mOptRecord.hasPendingCompact()) {
app.mOptRecord.setHasPendingCompact(true);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
}
} else {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM, "Skipping full compaction for " + app.processName
+ " oom adj score changed from " + app.mState.getSetAdj()
+ " to " + app.mState.getCurAdj());
}
}
}
3.1절에서 언급한 대로 여기에는 추가적인 조건부 판단이 추가됩니다.
4. 압축된 메시지 처리
메시지 처리 로직은 MemCompactionHandler .handleMessage() 함수에 위치하며 로직 은 기본적으로 Android R 버전과 동일하므로 여기서는 자세히 분석하지 않겠습니다.
주목해야 할 두 가지 사항이 있습니다.
- 비시스템 작업을 처리할 때 마지막으로 PerformCompaction() 함수가 호출됩니다.
- 시스템의 작업을 처리할 때 최종적으로 CompactSystem() 함수가 호출됩니다.
4.1 수행컴팩션()
public void performCompaction(String action, int pid) throws IOException {
if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
} else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
} else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
}
}
Android R 버전과 달리 기본 인터페이스는 여기에서 호출됩니다.
static private native void compactProcess(int pid, int compactionFlags);
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
jint compactionFlags) {
compactProcessOrFallback(pid, compactionFlags);
}
---->
CompactProcessOrFallback()
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
static void compactProcessOrFallback(int pid, int compactionFlags) {
// 入参compactionFlags 必须是ANON_FLAG 或 FILE_FLAG中的一种或两种
if ((compactionFlags & (COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG)) == 0) return;
// 确认是否有 anon 页回收,还是有file 页回收
bool compactAnon = compactionFlags & COMPACT_ACTION_ANON_FLAG;
bool compactFile = compactionFlags & COMPACT_ACTION_FILE_FLAG;
// Set when the system does not support process_madvise syscall to avoid
// gathering VMAs in subsequent calls prior to falling back to procfs
static bool shouldForceProcFs = false;
std::string compactionType;
VmaToAdviseFunc vmaToAdviseFunc;
// 根据压缩action,确认compactionType 和 回调函数
// 回调函数后面会调用到,主要返回 process_madvise() 所需要的类型 MADV_COLD 或MADV_PAGEOUT
if (compactAnon) {
if (compactFile) {
compactionType = "all";
vmaToAdviseFunc = getAnyPageAdvice;
} else {
compactionType = "anon";
vmaToAdviseFunc = getAnonPageAdvice;
}
} else {
compactionType = "file";
vmaToAdviseFunc = getFilePageAdvice;
}
// 如果compactProcess() 失败,即系统不支持 process_madvise()系统调用时,
// 通过shouldForceProcFs 变量控制,强制走 proc/PID/reclaim 节点驱动
if (shouldForceProcFs || compactProcess(pid, vmaToAdviseFunc) == -ENOSYS) {
shouldForceProcFs = true;
compactProcessProcfs(pid, compactionType);
}
}
---->
컴팩트프로세스()
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
ProcMemInfo meminfo(pid);
std::vector<Vma> pageoutVmas, coldVmas;
auto vmaCollectorCb = [&coldVmas,&pageoutVmas,&vmaToAdviseFunc](const Vma& vma) {
int advice = vmaToAdviseFunc(vma); //通过回调确定madvise的类型MADV_PAGEOUT或MADV_COLD
switch (advice) {
case MADV_COLD:
coldVmas.push_back(vma);
break;
case MADV_PAGEOUT:
pageoutVmas.push_back(vma);
break;
}
};
// 读取 /proc/PID/maps 确定vma并调用回调函数 vmaClooectorCb
meminfo.ForEachVmaFromMaps(vmaCollectorCb);
// 对进程中统计出来的 pageoutVma 进行 MADV_PAGEOUT 处理
int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
if (pageoutBytes < 0) {
// Error, just forward it.
return pageoutBytes;
}
// 对进程中统计出来的 coldVma 进行MADV_COLD处理
int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD);
if (coldBytes < 0) {
// Error, just forward it.
return coldBytes;
}
return pageoutBytes + coldBytes;
}
---->
컴팩트메모리()
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
// UIO_MAXIOV is currently a small value and we might have more addresses
// we do multiple syscalls if we exceed its maximum
static struct iovec vmasToKernel[UIO_MAXIOV];
if (vmas.empty()) {
return 0;
}
unique_fd pidfd(pidfd_open(pid, 0));
if (pidfd < 0) {
// Skip compaction if failed to open pidfd with any error
return -errno;
}
int64_t totalBytesCompacted = 0;
for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
vmasToKernel[iVec].iov_len = vmas[iVma].end - vmas[iVma].start;
}
auto bytesCompacted =
process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
if (CC_UNLIKELY(bytesCompacted == -1)) {
return -errno;
}
totalBytesCompacted += bytesCompacted;
}
return totalBytesCompacted;
}
각 vma는 process_madvise()를 사용하여 최종적으로 처리됩니다.
4.2 컴팩트시스템()
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) {
std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
struct dirent* current;
while ((current = readdir(proc.get()))) {
if (current->d_type != DT_DIR) {
continue;
}
// don't compact system_server, rely on persistent compaction during screen off
// in order to avoid mmap_sem-related stalls
if (atoi(current->d_name) == getpid()) {
continue;
}
std::string status_name = StringPrintf("/proc/%s/status", current->d_name);
struct stat status_info;
if (stat(status_name.c_str(), &status_info) != 0) {
// must be some other directory that isn't a pid
continue;
}
// android.os.Process.FIRST_APPLICATION_UID
if (status_info.st_uid >= 10000) {
continue;
}
int pid = atoi(current->d_name);
compactProcessOrFallback(pid, COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG);
}
}
최종 호출은 또한 CompactProcessOrFallback() 함수입니다.
5. 프로세스_마드바이스()
bionic/libc/include/sys/mman.h
ssize_t process_madvise(int picfd, const struct iovec* iovec, size_t vlen, int advice, unsigned flags);
process_madvise() 시스템 호출은 커널에서 프로세스 주소 영역 처리에 관한 제안이나 지침을 제공하는 데 사용됩니다.
Linux 5.10 버전을 지원해야 합니다.
이러한 제안은 시스템 또는 응용 프로그램 성능을 향상시키기 위한 것입니다.
주소 영역은 iovec 및 vlen 구조를 통해 관리됩니다.
매개변수:
- pidfd: 프로세스를 지정하는 데 사용되는 PID의 파일 설명자입니다.
- iovec: 구조 배열에 대한 포인터입니다.
- vlen: 구조 배열의 길이를 지정합니다. 이 값은 IOV_MAX 를 초과할 수 없습니다 .
- 조언: 다음 유형 중 하나:
- Linux 5.4부터 MADV_COLD는 지정된 페이지 범위를 비활성화하는 데 사용됩니다.
- MADV_COLLAPSE;
- Linux 5.4부터 MADV_PAGEOUT은 지정된 페이지 범위를 재생하는 데 사용됩니다.
- MADV_WILLNEED;
- 플래그: 예약되어 있으며 현재 기본적으로 0으로 설정되어 있습니다.
여기서 자세한 커널 호출은 크게 분석하지 않겠습니다. 모두 walk_page_range()를 호출하고, walk_ops도 cold_walk_ops입니다. 그러나 조언에 따라 논리가 약간 다릅니다(비활성화 및 회수 논리).
이제 Android S 버전의 앱 재활용 최적화에 대한 모든 분석이 완료되었으며 요약하면 다음과 같습니다.
- CachedAppOptimizer를 사용하여 앱 압축을 관리하고 다음과 같은 주요 방법으로 압축 인터페이스를 트리거합니다.
- 조정이 변경될 때 마지막 조정 <= PERCEPTIBLE_APP_ADJ가 PREVIOUS 또는 HOME 이 되면 일부 압축 최적화를 사용하세요.
- adj가 변경되면 지난번에 CACHED되지 않았으면 CACHED가 되고 FULL 압축 최적화가 사용됩니다.
- 비활성 상태이고 마지막 조정(setAdj)이 전경 조정( FOEGROUND_APP_ADJ )보다 더 중요하며 마지막 압축 간격이 10분을 초과하거나 압축된 적이 없는 경우 CompactAppPercious() 압축을 사용합니다. 최적화;
- 활성화되지 않은 상태이고 애플리케이션 프로세스 상태가 PROCESS_STATE_BOUND_FOREGROUND_SERVICE 이고 마지막 압축 이후 간격이 10분을 초과하거나 압축된 적이 없는 경우 CompactAppBfgs() 압축 최적화를 사용하십시오.
- AMS에서 FinishBooting()이 호출되면, 즉 시스템 시작이 완료된 후 시스템의 모든 프로세스가 완전히 압축되고 CompactAllSystem()이 호출됩니다 .
- MountServiceIdler는 startJob()이 시스템 압축을 수행할 때마다 AMS .performIdleMaintenance() 및 CompactAllSystem()을 호출합니다.
- CachedAppOptimizer는 압축이 호출된 후 전송된 메시지를 처리하기 위해 ServiceThread를 유지 관리합니다.
- 비시스템 압축은 공식적으로 압축 처리 기능에 들어가기 전에 많은 제한 사항이 필요합니다.
- 압축 작업은 압축 전에 병합됩니다. 일부는 mCompactActionSome 압축 방법을 사용하고 FULL / PERSISTENT / BFGS는 모두 mCompactActionFull 압축 방법을 사용합니다.
- 압축 처리 기능 포인트:
- PerformCompaction() , 기본 인터페이스 CompactProcess() 호출 시스템이 process_madvise()를 지원하는 경우 최적화를 위해 process_madvise()를 사용 하고 시스템 호출이 지원되지 않는 경우 Android R 메커니즘을 사용하여 /proc/PID/reclaim을 작성합니다. 드라이버 지원이 필요한 노드;
- CompactSystem() , 이는 기본 호출이며 궁극적으로 위와 동일하게 process_madvise()가 지원되는지 여부를 확인합니다.
Android R 버전의 압축 최적화에 대해서는 이전 블로그 게시물을 참조하세요 .
참고:
https://man7.org/linux/man-pages/man2/process_madvise.2.html