안드로이드 스마트폰에서 수많은 앱을 번갈아 실행하고, 백그라운드에서 음악을 들으며, 메모리가 부족할 때 우선순위가 낮은 앱이 자동으로 정리되는 일련의 과정들은 그냥 일어나는 것이 아닙니다. 샌드박스 형태로 철저히 격리된 개별 애플리케이션들의 라이프사이클을 실시간으로 감시하고, 한정된 시스템 메모리 자원을 분배하며, 응답이 없는 앱에 ANR 경고를 띄우는 절대적인 통제관이 백그라운드 내부에서 상시 가동되고 있기 때문입니다.
이 올마이티(Almighty)급 시스템 컴포넌트가 바로 ActivityManagerService(AMS)입니다. AMS는 안드로이드 아키텍처의 중심부인 system_server 프로세스 레이어 내부에서 상주하며 단말 전체의 프로세스 주권과 태스크 스택을 독점적으로 지배합니다. 고성능 앱을 설계하는 시니어 앱 개발자나 하드웨어 자원을 제어하는 벤더 엔지니어 모두에게 AMS 내부 구조 분석은 거쳐야 할 관문입니다. 본 포스팅에서는 AMS의 핵심 역할과 필수 데이터 구성 요소, 그리고 최신 AOSP(Android Open Source Project) 소스코드를 바탕으로 한 구동 메커니즘의 실체를 명쾌하게 해부해 보겠습니다.

📌 핵심 요약 3줄
- **ActivityManagerService(AMS)**는 안드로이드의 4대 컴포넌트(Activity, Service, Receiver, Provider)의 생명주기와 시스템 프로세스의 포크/소멸을 총괄하는 컨트롤 타워입니다.
- 내부적으로 ProcessRecord, ActivityTaskSupervisor, OomAdjuster 등의 객체 및 서브 매니저 모듈을 거미줄처럼 엮어 런타임 시스템 자원의 상태 지도를 실시간으로 유지합니다.
- AMS는 시스템 서버를 블로킹하는 무한 루프가 아니라, 전용 핸들러 스레드의 루퍼(Looper) 이벤트 구동 아키텍처를 채택하여 시스템 메시지를 비동기적으로 안전하게 처리합니다.
1. AMS 아키텍처를 이루는 5대 핵심 데이터 컴포넌트
안드로이드 커널과 가상 머신 계층 사이에서 애플리케이션의 물리적·논리적 상태를 증명하고 제어하기 위해 AMS가 내부적으로 유지하는 핵심 관리 객체 매트릭스입니다.
| 구성 요소 클래스 명칭 | 최신 AOSP 상주 경로 파이프라인 | 메모리 상에서 관리하는 핵심 시스템 메타데이터 정보 |
| ProcessRecord | com.android.server.am.ProcessRecord | 단일 OS 프로세스의 물리적 신분증. 커널이 할당한 PID, UID, 프로세스 명칭, 호스팅 중인 컴포넌트 목록 및 스레드 바인더 바인딩 주소 |
| ActivityTaskSupervisor | com.android.server.wm.ActivityTaskSupervisor | 멀티 디스플레이 및 전체 태스크 스택의 총감독. 단말에 띄워진 여러 개의 액티비티 스택 상태를 동기화하고 화면 전환 시 시퀀스 통제 |
| Task (레거시 TaskRecord 통합) |
com.android.server.wm.Task | 사용자 관점의 작업 그룹(유저 시나리오 단위). 최근 앱 버튼을 눌렀을 때 나타나는 카드 한 장에 대응되며, 논리적으로 적재된 액티비티들의 스택 관리 |
| BroadcastQueue | com.android.server.am.BroadcastQueue | 시스템 전역 이벤트 메시지 대기열. 포그라운드(고속) 및 백그라운드 큐로 분할 운영되며, 인텐트 브로드캐스트의 순차적 전달 마셜링 제어 |
| OomAdjuster | com.android.server.am.OomAdjuster | 메모리 부족 사태를 방어하는 심판관. 포그라운드/백그라운드/캐시 앱 상태에 따라 adj 스코어를 동적으로 계산하여 Low Memory Killer 드라이버에 투입 |
2. AOSP 코드로 파헤치는 AMS 부팅 초기화 및 이벤트 루프
2.1 SystemServer 커널 레이어에서의 AMS 탄생 시퀀스
안드로이드 단말이 전원이 켜진 후 가상 머신 엔진이 활성화되면, SystemServer 메인 부트스트랩 루프 내에서 AMS 인스턴스가 독점적으로 생성됩니다.
// frameworks/base/services/java/com/android/server/SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
t.traceBegin("StartActivityManager");
// [AOSP 표준] 최신 안드로이드 아키텍처는 SystemServiceManager 인터페이스를 통해
// AMS 의 라이프사이클 래퍼 클래스인 Lifecycle 을 구동하여 인스턴스를 주입합니다.
mActivityManagerService = SystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
// AMS 내부 시스템 전원 관리 및 설치 매니저 바인딩 연동 수행
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
t.traceEnd();
}
2.2 부트스트랩 런타임 활성화 메커니즘
생성 완료된 AMS 인스턴스는 정식 바인더 등록 절차를 밟고 시스템 전역에서 원격 호출을 받아들일 수 있도록 자격을 부여받습니다.
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
public Lifecycle(Context context) {
super(context);
// 시스템 컨텍스트를 주입하여 오리지널 AMS 싱글톤 객체를 생성합니다.
mService = new ActivityManagerService(context);
}
@Override
public void onStart() {
// 커널 영역의 ServiceManager 에 "activity" 라는 식별자로 바인더 등기 등록을 마칩니다.
mService.start();
}
}
2.3 비동기 이벤트 구동 루프의 진실
일부 명세에 잘못 표기된 while(true) 독점식 무한 루프와 달리, AMS는 프레임워크의 메인 스레드를 블로킹하지 않기 위해 비동기 링 버퍼 기반의 핸들러(Handler) 루퍼 체계를 사용하여 이벤트를 소비합니다.
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
// AMS 내부의 모든 비동기 상태 전환 요청을 안전하게 분할 처리하는 핵심 핸들러 클래스
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true); // 비동기 메시지 처리 활성화
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case START_USER_SWITCH_MSG:
mUserController.dispatchUserSwitch((UserInfo) msg.obj);
break;
case REPORT_ANR_MSG:
// 시스템으로부터 전달받은 특정 앱의 ANR 이벤트를 메인 스레드 정체 없이 비동기 처리
AppNotRespondingWithData data = (AppNotRespondingWithData) msg.obj;
appNotResponding(data.proc, data.activity, data.parent, data.aboveSystem, data.annotation);
break;
case PROC_START_TIMEOUT_MSG:
// Zygote 에 요청한 리눅스 프로세스 포크가 일정 시간 안에 응답이 없을 때 타임아웃 킬 처리
processStartTimedOutLocked((ProcessRecord) msg.obj);
break;
}
}
}
3. AMS 핵심 3대 기능의 소스코드 심층 분석
3.1 OomAdjuster를 통한 실시간 메모리 우선순위 재계산
안드로이드는 메모리가 한계치에 다다랐을 때 무작위로 앱을 죽이지 않습니다. OomAdjuster가 계산한 가치 등급표(adj)에 근거하여 철저하게 숙청 대상을 정합니다.
// frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
@GuardedBy("mService")
protected final boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
// 해당 프로세스의 현재 윈도우 포커스 유무, 포그라운드 서비스 활성화 여부를 역추적하여 점수를 연산합니다.
int cachedAdj = computeOomAdjLocked(app, OomScores.PROCESS_STATE_CUR_TOP, ...);
// 계산된 조정 수치를 ProcessRecord 에 업데이트합니다.
app.setCurRawAdj(cachedAdj);
// 만약 계산된 등기가 백그라운드 하위 등급이라면 리눅스 Low Memory Killer(lmkd) 소켓 인터페이스로
// 우선순위 스코어를 주입하여 커널 단에서 메모리 압착 시 즉시 킬(Kill)이 가능하도록 세팅합니다.
if (app.getCurRawAdj() != app.getSetRawAdj()) {
mService.mProcessList.writeLmkdOomAdjSet(app.getPid(), app.getUid(), cachedAdj);
app.setSetRawAdj(cachedAdj);
return true;
}
return false;
}
3.2 ActivityStarter 인터페이스를 통한 런칭 트래픽 접수
유저가 앱 아이콘을 터치하면 인텐트(Intent)의 유효성을 검증하고 실제 런칭 파이프라인을 기동하는 입구 인터페이스입니다.
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
@Override
public int execute() {
synchronized (mService.mGlobalLock) {
// 호출한 클라이언트의 신원을 확인하고 권한 샌드박스 검증을 수행합니다.
final ActivityRecord r = new ActivityRecord.Builder(mService)
.setIntent(mRequest.intent)
.setActivityInfo(mRequest.activityInfo)
.setCaller(mRequest.caller)
.build();
// 검증이 완료되면 ActivityTaskSupervisor 와 결합하여 태스크 스택을 생성하거나 최상단에 푸시합니다.
return executeRequest(r);
}
}
3.3 ANR(애플리케이션 무응답) 모니터링 및 프로세스 사후 처리
UI 메인 스레드가 5초 이상(브로드캐스트는 10초) 무반응 상태로 홀딩될 때, 이를 시스템 에러로 인지하고 다이얼로그를 호출하는 방어 로직입니다.
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
void appNotResponding(ProcessRecord app, String annotation) {
// 동시다발적인 ANR 처리를 방지하기 위해 프로세스의 현재 상태가 이미 킬 상태인지 방어 체크
if (app.isKilledByAm() || app.isKilled()) {
return;
}
Slog.w(TAG, "ANR 감지 프로세스 명칭: " + app.processName + " | 원인 세부 정보: " + annotation);
// 디버깅 및 에러 리포팅을 위해 현재 프로세스의 스레드 콜스택 트레이스(Trace) 덤프 생성
File tracesFile = ActivityManagerService.dumpStackTraces(app);
// 메인 UI 스레드 핸들러 영역으로 에러 알림 메시지를 던져 사용자에게 ANR 종료/대기 대화상자 표출
mUiHandler.post(() -> {
mAppErrors.handleAppNotResponding(app, annotation);
});
}
4. 앱 전환에 따른 AMS 내부 데이터 갱신 시퀀스
포그라운드 앱이 백그라운드로 내려가고 새로운 앱이 올라올 때, AMS 내부의 컴포넌트들이 바인더 트래픽을 처리하는 동적 실행 시퀀스입니다.
- 상태 전환 인지: 유저가 홈 버튼을 누르면 ActivityStarter가 구동되며 기존 최상단 액티비티를 PAUSING 상태로 천명합니다.
- 태스크 스택 정렬: ActivityTaskSupervisor가 전체 디스플레이 리스트의 스택 구조를 동적으로 재정렬하여 홈 화면 혹은 신규 앱의 태스크를 액티브 탑(TOP) 위치로 교체 배정합니다.
- 메모리 등급 등락 연산: 화면에서 가려진 레거시 ProcessRecord 인스턴스 정보가 OomAdjuster로 전달됩니다.
- LMK 커널 드라이버 소켓 통신: OomAdjuster는 해당 프로세스의 등급 수치를 즉시 PROCESS_STATE_HOME 또는 CACHED 레벨로 대폭 떨어뜨린 뒤, LMKD(Low Memory Killer Daemon) 파이프라인에 주입하여 커널 레벨의 잠재적 숙청 대기 명단에 등재합니다.
💡 안드로이드 프레임워크 최적화를 위한 실전 팁
- 시스템 커스텀 시 ActivityManagerConstants 파라미터 튜닝을 통한 저사양 단말 최적화: 초소형 임베디드 단말이나 메모리가 극도로 제한된 IoT 안드로이드 보드를 튜닝할 때, 기본 킬러 정책이 너무 둔감하여 OOM 크래시가 빈번히 터질 수 있습니다. 이럴 때는 frameworks/base/services/core/java/com/android/server/am/ActivityManagerConstants.java 소스코드의 전역 환경 변수를 직접 튜닝해 보세요. 백그라운드에 상주할 수 있는 최대 캐시 프로세스 개수 한계값(MAX_CACHED_APPS)을 기본 32개에서 단말의 RAM 사양에 맞춰 8개~12개 수준으로 다이어트하고, ANR 판단 타임아웃 주기를 조절하면 저사양 하드웨어 환경에서도 시스템 서버가 뻗지 않고 안정적인 가용 메모리를 유지할 수 있습니다.
- adb shell dumpsys activity processes 명령어를 통한 프로세스 런칭 라이브 플로우 추적: 내가 수정한 AMS 커스텀 코드나 새로 빌드한 백그라운드 서비스의 adj 점수 변화를 정밀 분석하고 싶다면 디버그 덤프 명령어를 사용해 보세요. 개발 보드 터미널에 해당 명령을 입력하면 현재 가동 중인 모든 ProcessRecord 객체의 실시간 PID, UID 상태는 물론, OomAdjuster에 의해 실시간 변동되는 oomadj 점수(OOM_ADJ_SCORE)와 런타임 프로세스 우선순위 스택 변동 추이가 텍스트 파일 구조로 완벽히 가시화되어 출력되므로 로그캣 공해 없이 정확한 디버깅이 가능합니다.
⚠️ 흔히 하는 실수
- AMS 전용 MainHandler 메시지 가로채기(Blocking)로 인한 전 단말 시스템 ANR 유발: AMS 코드를 직접 커스텀하는 플랫폼 개발자들이 성능 프로파일링이나 예외 처리를 추가할 때 가장 빈번하게 저지르는 대형 아키텍처 예외 사고입니다. AMS 내부의 비동기 이벤트를 중재하는 MainHandler 구문 내부에 특정 하드웨어 드라이버의 제어를 동기식으로 기다리는 입출력(I/O) 대기 코드를 직접 박아 넣는 행위입니다. AMS의 핸들러 메시지 큐가 수 초간 멈춰 서 버리면 단말 내부의 모든 앱 실행, 브로드캐스트 전송, 태스크 스위칭 요청 처리가 연쇄적으로 지연되며, 화면 전체가 멈추고 시스템 프레임워크 자체에서 전방위적 ANR(System Server Is Not Responding)을 터트리며 OS가 프리징(Freezing)됩니다. 시간이 소요되는 커스텀 사후 연산은 무조건 독자적인 워커 스레드로 분할 주입해야 합니다.
- ProcessRecord 바인더 바인딩 객체의 생명주기 관리 실패로 인한 영구적 메모리 릭(Memory Leak): AMS 내부에서 특정 프로세스의 가동 유무를 커스텀 리스트나 맵 구조로 추적하기 위해 ProcessRecord 객체 포인터를 정적(Static) 콜렉션 구조에 그대로 복사해 둔 뒤, 해당 앱이 종료(processStartTimedOutLocked 또는 일반 킬)되었음에도 명시적으로 제거(Remove)해 주지 않을 때 터지는 고질적인 누수 예외입니다. 시스템 서버의 힙 메모리에 좀비처럼 남아있는 ProcessRecord 레퍼런스는 자바 가비지 컬렉터(GC)의 수거 대상에서 영원히 누락되며, 단말기를 끄기 전까지 수많은 앱이 켜고 꺼질 때마다 자바 가비지 컬렉션 오버헤드를 유발하고 점진적으로 시스템 서버의 메모리를 고갈시켜 스마트폰이 스스로 재부팅되는 참사를 발생시킵니다.
5. 결론
Android Framework의 절대 권력자인 ActivityManagerService는 운영체제 내부의 가상화 경계와 물리 리눅스 커널 프로세스 스택을 관통하여 최적의 앱 구동 생태계를 유지해 내는 고도의 컴포넌트 지휘소입니다. 시스템 서버 부팅 단계의 비동기 핸들러 동작 구조를 올바르게 인지하고, OomAdjuster가 연산해 내는 프로세스 메타데이터의 흐름을 AOSP 소스코드 레벨에서 유연하게 통제할 수 있을 때, 비로소 하드웨어의 모든 잠재력을 끌어올리는 혁신적인 커스텀 OS 레이어와 프레임워크 엔진을 완벽하게 구축해 낼 수 있습니다.
'Android System & AOSP Engineering > AOSP Framework & Custom Services' 카테고리의 다른 글
| 안드로이드 PMS 구조 분석: PackageManagerService의 APK 설치 메커니즘과 AOSP 소스코드 해부 (0) | 2025.04.10 |
|---|---|
| 안드로이드 WMS 구조 분석: WindowManagerService의 윈도우 관리와 AOSP 레이어링 메커니즘 (0) | 2025.04.09 |
| 안드로이드 프레임워크 구조 분석: AMS, WMS, PMS 핵심 시스템 서비스와 AOSP 소스코드 탐구 (0) | 2025.04.07 |
| 안드로이드 프레임워크 가이드: AOSP 시스템 서비스 구축과 JNI 기반 System API 확장 (0) | 2025.04.06 |
| 안드로이드 NDK 빌드 가이드: CMake 환경 설정부터 최신 ABI 성능 최적화까지 (0) | 2025.04.05 |