Android의 앱 프로세스는 격리되어 존재하지 않습니다. 애플리케이션은 다른 애플리케이션 또는 시스템 자체에서 제공하는 서비스를 사용하는 경우가 많습니다. 한 프로세스가 서비스 결합을 통해 다른 프로세스에 연결되면 Android 프레임워크가 메모리를 관리하는 방식에 큰 영향을 미치는 종속 항목이 생성됩니다.
프로세스 상태 및 OOM 점수
Android 프레임워크는 프로세스 상태 를 사용하여 실행 중인 각 프로세스의 중요도를 추적합니다. 이러한 상태는 OomAdjuster에서 -1000~1000 범위의 OOM 점수 조정 (oom_score_adj) 값을 할당하는 데 사용됩니다.
oom_score_adj가 낮을수록 프로세스가 더 중요하며 로우 메모리 킬러 (LMK)에 의해 종료될 가능성이 낮습니다.
일반적인 프로세스 상태
다음 표에서는 가장 일반적인 프로세스 상태와 일반적인 oom_score_adj 값을 보여줍니다. 전체 최신 목록은 Android 소스 코드의 android.app.ActivityManager 및 com.android.server.am.psc.Constants를 참고하세요.
| 프로세스 상태 (약어) | 설명 | 일반적인 oom_score_adj |
|---|---|---|
| PER (영구) | 항상 실행되어야 하는 시스템 프로세스 (예: 전화 통신). | -800 |
| TOP | 사용자가 현재 상호 작용하고 있는 프로세스입니다. | 0 |
| VIS (표시) | 프로세스에 표시되는 활동이 있습니다 (예: 반투명 대화상자 뒤). | 100 |
| PERC (인지) | 사용자가 알고 있는 백그라운드 프로세스 (예: 음악 재생). | 200 |
| FGS | 포그라운드 서비스를 호스팅하는 프로세스입니다. | 0~200 (다름) |
| BTOP (결합된 상위) | TOP 애플리케이션에 의해 결합된 프로세스입니다. | 100 |
| BFGS | 결합된 포그라운드 서비스 (일반적으로 시스템 결합). | 0 |
| PREV (이전) | 사용자가 현재 프로세스 전에 사용한 마지막 프로세스입니다. | 700 |
| CACHED | 안전하게 종료할 수 있는 백그라운드 앱입니다. | 900~999 |
서비스 결합의 영향
클라이언트 프로세스 (예: TOP 상태의 앱)가 서버 프로세스의 서비스에 결합되면 서버 프로세스는 종종 높은 우선순위를 상속 합니다. 이렇게 하면 클라이언트가 서비스를 필요로 하는 한 서비스를 계속 사용할 수 있습니다.

BIND 플래그로 상속 제어
상속은 Context.BIND_AUTO_CREATE를 사용할 때의 기본 동작입니다.
하지만 개발자는 bindService()의 다양한 플래그를 사용하여 결합이 타겟 프로세스의 중요도에 미치는 영향을 제어할 수 있습니다.
OOM 점수의 주요 BIND 플래그
다음 플래그는 시스템 전체 메모리 압력을 관리할 때 가장 관련성이 높습니다.
BIND_AUTO_CREATE: 가장 일반적인 플래그입니다. 결합이 존재하는 한 서비스 프로세스가 시작되고 활성 상태로 유지되도록 합니다. 기본적으로 서버 프로세스 우선순위도 클라이언트와 일치하도록 높입니다.BIND_NOT_FOREGROUND: 타겟 서비스의 프로세스가 포그라운드 예약 우선순위 (CPU 우선순위)로 올라가지 않도록 합니다. 하지만 메모리 우선순위 (oom_score_adj)는 여전히 높일 수 있습니다. 이는 CPU 주기에 대해 UI와 경쟁해서는 안 되지만 종료되지 않도록 보호해야 하는 백그라운드 작업에 유용합니다.BIND_WAIVE_PRIORITY: 시스템이 타겟 프로세스의 예약 또는 메모리 관리 우선순위에 영향을 미치지 않도록 지시하는 매우 강력한 플래그입니다. 서비스 프로세스는 LRU 목록의 일반적인 백그라운드 프로세스인 것처럼 관리되므로 결합된 상태에서도 OOM 종료 대상이 됩니다.BIND_ABOVE_CLIENT: 서비스가 클라이언트 앱 자체보다 더 중요함을 나타냅니다. 시스템에서 메모리를 회수해야 하는 경우 바인드된 서비스를 종료하기 전에 클라이언트 앱을 종료하는 것을 선호합니다. 이는 클라이언트의 비용으로 서비스에 추가 보호 레이어를 제공하므로BIND_AUTO_CREATE보다 '강력'합니다.BIND_NOT_PERCEPTIBLE: 타겟 서비스의 중요도를PERCEPTIBLE수준 아래로 낮춰 시스템에서 메모리를 회수하여 더 중요한 사용자 인지 프로세스를 위한 공간을 확보할 수 있도록 합니다.
실습: 결합 효과 관찰
MemoryLab 애플리케이션을 사용하여 TOP 앱의 결합이 별도의 프로세스 상태에 미치는 영향을 보여드리겠습니다.
1. MemoryLab 실행
다음 명령어는 앱을 실행합니다. 앱이 열리면 앱이 포그라운드에 유지되도록 합니다 (아직 홈을 누르거나 앱을 전환하지 마세요).
adb shell am start -n com.android.memorylab/.MainActivity
2. 프로세스 식별
결합 전에 프로세스 상태를 확인합니다. MemoryLab 은 하나의 프로세스에서 기본 UI를 실행하고 :remote 프로세스에서 실행되는 RemoteService를 보유합니다.
adb shell dumpsys activity processes com.android.memorylab
출력 스니펫 샘플:
Process OOM control (154 total, non-act at 7, non-svc at 7):
Proc #0: fg T/A/TOP LCMNFUATI t: 0 13470:com.android.memorylab/u0a417 (top-activity)
oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0
state: cur=TOP set=TOP lastRss=0.00 lastCachedRss=0.00
TOP 상태의 기본 프로세스 com.android.memorylab이 표시됩니다. :remote 프로세스는 아직 시작되지 않았습니다.
3. 결합 트리거
앱에 브로드캐스트를 전송하여 서비스 결합을 트리거합니다.
adb shell am broadcast -a com.android.memorylab.LEAK_BINDER
4. 높은 상태 관찰
프로세스 상태를 다시 확인합니다.
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
출력 스니펫 샘플:
Proc # 1: vis F/ /BTOP ---NFUATI t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100
state: cur=BTOP set=BTOP lastRss=0.00 lastCachedRss=0.00
이제 :remote 프로세스가 실행 중이며 BTOP (결합된 상위) 상태이고 oom_score_adj 가 100 입니다. 이는 일반적인 백그라운드 서비스 (500 이상)보다 훨씬 더 보호됩니다.
<=Proc{...} 표기법은 이 우선순위 상승을 담당하는 프로세스를 보여줍니다.
5. 백그라운드로 전송
기기의 홈 버튼을 누릅니다. 상태를 다시 확인합니다.
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
출력 스니펫 샘플:
Proc # 2: prev b/ /LAST --------I t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=0.00 lastCachedRss=0.00
Proc # 1: prev b/ /LAST --------I t: 0 13470:com.android.memorylab/u0a417 (previous)
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=209MB lastCachedRss=0.00
이제 클라이언트 프로세스가 더 이상 TOP이 아니므로 두 프로세스 모두 우선순위가 낮은 상태 (PREV / oom_score_adj 700)로 전환되었습니다. (참고: 상태 덤프의
LAST는 LAST_ACTIVITY 내부 상태를 나타내며, 이는
PREV 상위 요약에 매핑됩니다.)
procstats로 분석
procstats 도구는 이러한 상태의 기록 보기를 제공합니다.
# View stats for MemoryLab over the last hour
adb shell dumpsys procstats --hours 1 com.android.memorylab
출력 스니펫 샘플:
* com.android.memorylab / u0a417 / v37:
* Prc com.android.memorylab / u0a417 / v37:
TOTAL: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
Top: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
* Prc com.android.memorylab:remote / u0a417 / v37:
TOTAL: 0.19%
Bnd Top: 0.19%
여기서 Bnd Top 은 원격 프로세스가 TOP 상태의 애플리케이션에 의해 결합된 상태로 보낸 시간의 비율을 나타냅니다.
Perfetto로 결합 캡처 및 분석
dumpsys는 스냅샷을 제공하지만 Perfetto를 사용하면 결합이 발생하는 정확한 순간과 OOM 점수가 실시간으로 변경되는 방식을 확인할 수 있습니다.
1. 트레이스 기록
linux.process_stats 및 am atrace 카테고리가 포함된 구성을 사용합니다.
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/service_bindings.perfetto-trace <<EOF
buffers: { size_kb: 65536 }
data_sources: {
config {
name: "linux.process_stats"
process_stats_config { proc_stats_poll_ms: 100 }
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config { ftrace_events: "am/am_proc_bound" }
}
}
duration_ms: 15000
EOF
2. OOM 점수 전환 쿼리
PerfettoSQL을 사용하면 UI 프로세스와 관련하여 원격 프로세스의 OOM 점수가 어떻게 변경되었는지 확인할 수 있습니다.
SELECT ts, p.name, value AS oom_score_adj
FROM counter c
JOIN process_counter_track t ON c.track_id = t.id
JOIN process p USING (upid)
WHERE p.name LIKE 'com.android.memorylab%'
AND t.name = 'oom_score_adj'
ORDER BY ts;
3. 결합 이벤트 식별
결합 종속 항목이 설정된 정확한 시점과 이를 시작한 프로세스를 확인하려면 다음 쿼리를 사용하세요.
SELECT
s.ts,
p.name AS process_name,
t.name AS thread_name,
s.name AS slice_name
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t USING (utid)
JOIN process p USING (upid)
WHERE s.name LIKE 'bindService:{com.android.memorylab%';
시스템-앱 결합
Android 시스템 자체는 핵심 기능을 제공하기 위해 서드 파티 앱의 서비스에 결합되는 경우가 많습니다. 이러한 결합의 목표는 지연 시간 단축 인 경우가 많습니다. 프로세스를 활성 상태로 유지하고 메모리에 보관함으로써 시스템은 중요한 사용자 상호작용이 발생할 때 '콜드 스타트'(APK 로드, 런타임 초기화, 애플리케이션 객체 생성)의 비용이 많이 드는 오버헤드를 방지합니다. 백그라운드 이벤트 스트림을 처리해야 하는 앱의 콜드 스타트가 자주 발생하는 것을 방지하기 위한 다른 결합도 있습니다.
다음은 일반적인 기기에서 관찰할 수 있는 실제 예입니다.
VoiceInteractor
사용자는 디지털 어시스턴트가 휴대전화 OS에 내장되어 음성 핫워드 또는 빠른 입력 동작으로 즉시 호출할 수 있고 상호작용이 원활하고 원활하게 이루어지기를 기대합니다.
어시스턴트 트리거 (예: Google Pixel 휴대전화의 'OK Google' 핫워드)가 발생하면 디지털 어시스턴트가 즉시 응답해야 합니다. 이를 위해
system_server는 사용자가 선택한 음성 상호작용
서비스에 대한 영구 결합을 유지합니다.

프로세스 상태 (예: dumpsys activity processes 사용)를 확인하면 system_server (UID 1000)의 결합으로 활성 상태를 유지하는 BFGS (결합된 포그라운드 서비스) 상태의 com.google.android.googlequicksearchbox:interactor와 같은 프로세스가 표시될 수 있습니다.
NotificationListenerService
일부 시스템-앱 결합의 목표는 지연 시간이 아니라 콜드 스타트가 자주 발생하는 것을 방지하는 것입니다.
NotificationListenerService, 새 알림이 게시되거나 삭제될 때 시스템에서 호출을 수신하는 서비스인
가 대표적인 예입니다. 일반적인 스마트폰 사용자는 하루 종일 수백 개의 알림을 받을 수 있습니다. 시스템이 알림 리스너에서 언바운드되면 해당 앱의 프로세스가 캐시된 상태로 전환되고 LMK에 의해 종료될 수 있습니다.
다음 알림이 도착하면(몇 초 후일 수 있음) 시스템은 이벤트를 전달하기 위해 앱의 프로세스를 다시 콜드 스타트해야 합니다. 이러한 종료 및 콜드 스타트의 지속적인 주기는 프로세스를 백그라운드에서 결합하고 활성 상태로 유지하는 것보다 훨씬 더 많은 CPU와 배터리를 소모합니다.
런처의 '-1 화면'(뉴스 피드)
최신 런처 앱은 일반적으로 핵심 탐색 기능 (홈 아이콘 및 위젯)과 런처 화면 중 하나에서 사용할 수 있고 런처 UX와 원활하게 통합되는 뉴스 피드를 결합합니다. 뉴스 피드는 다른 앱에서 제공할 수 있습니다. 예를 들어 Google Pixel에서 런처는 Google 앱에서 제공하는 피드와 통합됩니다.
홈 화면에서 왼쪽으로 스와이프하여 뉴스 피드를 볼 때 전환이 원활해야 합니다. 런처는 뉴스 피드를 제공하는 앱의 서비스 인터페이스에 결합하고 런처가 활성 상태인 한 해당 결합을 활성 상태로 유지하여 이를 달성합니다. 이렇게 하면 피드 콘텐츠가 메모리에 렌더링되고 준비된 상태로 유지되므로 사용자가 피드를 보고 있지 않을 때도 마찬가지입니다.
기타 일반적인 예
- 런처 (HOME_APP_ADJ): 런처 앱 (홈)에는 우선순위 목록에 자체 특수
슬롯이 있습니다. 서비스에 의해 항상 결합되지는 않지만
HOME_APP_ADJ(일반적으로 600)가 할당됩니다. 사용자가 자주 돌아가기 때문에 시스템은 런처를 활성 상태로 유지하는 것을 선호합니다. 실제로 시스템은 런처를 종료하는 것보다 이전에 사용한 앱 (PREV_APP_ADJ = 700)을 종료하는 것을 선호합니다. 런처를 종료하면 사용자가 런처가 콜드 스타트될 때까지 기다려야 하므로 앱을 종료할 때 사용자 환경이 느려지기 때문입니다. - 입력 방식 편집기 (IME): 입력할 때 시스템은 선택한 키보드 앱 (예: Gboard)에 결합됩니다. 이렇게 하면 키보드가 일시적으로 숨겨져 있어도 키보드 프로세스가 높은 상태로 유지됩니다. 이렇게 하면 다른 텍스트 필드를 탭할 때 키보드가 즉시 다시 나타날 수 있습니다.
- NFC 결제: 휴대전화를 탭하여 결제할 때 시스템은 NFC 결제 서비스 (예: Google 월렛)에 결합됩니다. 이러한 트랜잭션에는 가맹점 단말기의 엄격한 실시간 요구사항이 있는 경우가 많습니다. 결제 앱을 콜드 스타트해야 하는 경우 트랜잭션이 시간 초과되어 실패할 수 있습니다.
절충점 및 성능 절벽
결합은 성능과 정확성에 필요하지만 시스템의 메모리 상태에 비용이 듭니다.
- 유연성 감소: 결합된 모든 프로세스는 LMK가 쉽게 종료할 수 없는 프로세스입니다. 이렇게 하면 시스템에서 압력을 받는 메모리를 확보하는 데 사용할 수 있는 캐시된 프로세스의 '쿠션'이 줄어듭니다.
- 성능 절벽 악화: 너무 많은 프로세스가 결합되면 시스템에 종료 가능한 백그라운드 프로세스가 거의 없을 수 있습니다. 메모리 압력이 증가하면 시스템은 더 중요한 프로세스를 종료하거나 페이지 캐시를 스래싱해야 하므로 훨씬 더 빠르게 '성능 절벽에서 떨어집니다'.