로그 분석 대시보드 Android logcat 파일 분석
← 아티클 목록

앱이 크래시 없이 사라질 때 — lmkd 강제 종료와 oom_score_adj 읽기

심화 · 메모리 부족 · lowmemorykiller/lmkd · 2026년 10월 5일

"앱이 꺼졌는데 크래시 기록이 없다", "편성표를 다시 열면 처음부터 로딩된다", "백그라운드 음악이 끊겼다". 메모리가 넉넉하지 않은 셋톱박스·TV에서 자주 듣는 제보입니다. 대개 앱의 버그가 아니라 기기 전체 메모리가 부족해 시스템이 프로세스를 강제로 종료한 결과입니다. 이 기록은 오류(E) 레벨이 아니라서 오류만 세면 보이지 않습니다. 이 글은 그 기록을 찾고 읽는 법, 그리고 "누가 메모리를 차지했는지"까지 좁혀 가는 순서를 정리합니다.

1. 두 가지 메모리 부족은 다르다

앱 힙 초과기기 전체 메모리 부족
로그java.lang.OutOfMemoryError (E)lowmemorykiller/lmkd의 Kill '...' (주로 I)
누가 죽나한도를 넘긴 그 앱(크래시로 종료)우선순위가 낮은 다른 프로세스들
원인그 앱이 큰 이미지·버퍼를 붙들고 있음, 누수기기 전체 사용량 — 범인은 죽은 앱이 아닐 수 있음
대시보드 패턴oomlmk-kill + proc-died

두 번째 경우에 강제 종료된 앱의 코드를 아무리 들여다봐도 원인은 나오지 않습니다. 그 시각 메모리를 많이 쓰던 다른 프로세스를 찾아야 합니다.

2. 강제 종료 기록 읽기

안드로이드는 원래 커널의 lowmemorykiller 드라이버가 이 일을 했고, 리눅스 커널 4.12에서 그 드라이버가 제거된 뒤로는 사용자 영역의 lmkd 데몬이 맡습니다. 그래서 기기에 따라 기록 형식이 두 가지입니다.

# 최근 형식 (lmkd, logcat 태그는 대개 lowmemorykiller)
I lowmemorykiller: Kill 'com.example.epg' (1650), uid 10031, oom_score_adj 900 to free 48212kB rss, 2048kB swap

# 옛 형식 (커널 드라이버 시절, 주로 커널 로그에 남음)
lowmemorykiller: Killing 'com.example.epg' (1650), adj 900, to free 48212kB on behalf of 'kswapd0' (29) ...

옛 형식은 커널 로그(dmesg)에만 남는 경우가 많아 logcat 파일에는 없을 수 있습니다. 이 대시보드는 logcat만 읽으므로, 그런 기기에서는 강제 종료가 있어도 패턴이 잡히지 않습니다.

강제 종료 직후에는 대개 시스템 서버가 ActivityManager: Process com.example.epg (pid 1650) has died: cch+5 CEM 같은 기록을 남깁니다. 반대로 has died는 있는데 앞에 크래시도 강제 종료도 없다면 다른 원인(네이티브 크래시, 앱 스스로 종료 등)을 찾아야 합니다.

3. oom_score_adj로 무엇이 죽었는지 가늠하기

값의 범위는 -1000~1000이고 클수록 먼저 종료됩니다. 안드로이드 프레임워크는 프로세스 상태에 따라 대략 다음 값을 매깁니다(정확한 값과 단계는 버전·제조사마다 다를 수 있음).

값(대략)상태종료되면 사용자가 느끼는 것
음수시스템·네이티브·상주 프로세스거의 종료되지 않음. 종료됐다면 매우 심각
0지금 화면에 보이는 앱보고 있던 앱이 꺼짐 — 메모리 부족이 극심했다는 뜻
100~200보이는 앱, 인지 가능한 앱(백그라운드 재생 등)배경 음악·라디오 끊김
500 안팎서비스 프로세스동기화·다운로드 같은 백그라운드 작업 중단
600~700홈(런처), 직전에 쓰던 앱홈으로 돌아가면 런처가 다시 그려짐, "뒤로"가 느림
900~999캐시된 백그라운드 앱다시 열면 처음부터 로딩 — 정상적인 동작에 가까움

900 이상의 캐시 앱이 가끔 종료되는 것은 안드로이드가 원래 메모리를 관리하는 방식이라 장애로 보기 어렵습니다. 문제는 값이 작은 프로세스까지 종료될 때입니다. 런처(600대)나 보이는 앱(0~100)이 종료됐다면, 그 시각 메모리 압박이 매우 컸다는 증거입니다.

4. 연쇄 종료 패턴 보기

한 건보다는 짧은 시간 안에 몇 건이 연달아 났는지가 중요합니다.

대시보드의 감지된 장애 패턴 카드에서 메모리 확보용 강제 종료의 처음·마지막 시각과 대상 목록을 보면 이 패턴이 바로 드러납니다.

5. 누가 메모리를 차지했는지 확인하기

logcat만으로는 각 프로세스의 사용량을 알 수 없습니다. 증상이 재현되는 동안 다음 명령으로 상태를 함께 남겨 두세요.

adb shell dumpsys meminfo              # 프로세스별 메모리 사용량(PSS) 순위
adb shell dumpsys meminfo com.example.player   # 특정 앱의 자세한 내역
adb shell cat /proc/meminfo            # 기기 전체: MemTotal, MemAvailable 등
adb shell getprop ro.config.low_ram    # true면 저사양(low-RAM) 기기 설정

6. 보고서에 남길 것

- 강제 종료 시각 범위와 건수 (예: 20:15:09~20:15:40, 2건)
- 종료된 프로세스와 각 oom_score_adj, 확보량(kB)
- 가장 작은 oom_score_adj (화면에 보이던 앱까지 종료됐는지)
- 같은 시각 사용자 동작 (재생 시작, 앱 설치, 목록 스크롤 등)
- 증상 전후 dumpsys meminfo 상위 5개 프로세스
- 기기 메모리 총량, low_ram 여부, 펌웨어 버전

정리

참고: lowmemorykiller 드라이버 제거와 lmkd 전환은 Android Open Source Project의 lmkd 문서(source.android.com의 "Low memory killer daemon")에 설명돼 있습니다. oom_score_adj 단계 값은 안드로이드 프레임워크의 프로세스 우선순위 정의를 기준으로 한 대략값입니다.

관련 글
· 실습: 셋톱박스 2대의 로그를 대시보드로 읽어 보기
· 네이티브 크래시 읽는 법
· 저장된 로그에서 원인 구간 좁히기
· 아티클 목록 · 패턴 사전 · 서비스 소개
문의: dkdnj123@gmail.com