CS Insights

운영체제 메모리 스와핑(Swapping) 데드락과 OOM(Out of Memory) 킬러의 이기적인 생존 메커니즘

운영체제 메모리 스와핑(Swapping) 데드락과 OOM(Out of Memory) 킬러의 이기적인 생존 메커니즘
팀 내 세미나를 준비하면서 여러 논문과 레퍼런스를 뒤져봤는데, 한글로 속 시원하게 정리된 곳이 없더군요. 그래서 제 경험을 녹여 직접 작성해 봤습니다. 메인 메모리(RAM)는 언제나 소프트웨어의 탐욕스러운 요구를 만족시킬 수 없는 부족한 자원입니다. 운영체제의 VMM(Virtual Memory Manager)은 물리 레지스터가 모두 소모된 백엔드 상황에서도 응용 프로그램들을 강제로 종료시키지 않기 위해, 현재 당장 사용되지 않는 비활성 메모리 페이지 구간을 느린 디스크의 스왑(Swap) 파티션으로 밀어내는 페이징 아웃(Paging Out) 기법을 상시 구동합니다. 커널 내부의 백그라운드 프로세스인 kswapd는 시스템 메모리의 여유분(Free Watermark)이 위험 수준으로 떨어지면 적극적으로 개입하여 LRU(Least Recently Used) 기반 알고리즘 서브 리스트를 훑어 희생양 페이지를 찾습니다. 그러나 이 과정에서 디스크 I/O 스루풋 한계로 인해 스왑 영역을 할당하는 속도가 어플리케이션이 메모리를 먹어치우는 속도를 도저히 따라가지 못하는 악성 스레싱(Thrashing)이 촉발되기도 합니다. 이때 극단적인 시스템 멈춤을 넘어서 스왑 알고리즘마저 한계를 넘는 순간, 커널은 이른바 OOM(Out of Memory) 상태에 진입합니다. 시스템 전체가 커널 패닉으로 뻗어버리는 최악의 사태를 막기 위해 리눅스는 아주 무자비하지만 효율적인 경찰 프로세스인 OOM Killer 엔진을 호출시킵니다. OOM Killer의 존재 목적은 단 하나, 현재 호스트 내의 프로세스들 중 운영체제를 살리기 위해 가장 적합한 거대 용량 희생양을 색출하여 SIGKILL 시그널을 보내 즉결 처형하는 것입니다. 판단의 기준은 단순히 누가 메모리를 가장 많이 먹었는가에 그치지 않고, 그 프로세스의 권한, 동작 기간, 루트(Root) 계정 여부 등을 종합 평가하는 oom_score 점수 체계에 따릅니다. 무자비한 사냥이 끝나고 메모리가 회수되면 커널은 헐떡이며 겨우 시스템 콜 인터랙션을 재개합니다. 이 기능은 데스크톱 환경에서는 컴퓨터가 다운되는 것을 막아주는 구원자일지 모르나, 막대한 데이터베이스 인 메모리 캐시를 돌리는 Redis 인스턴스나 JVM 기반의 마이크로서비스 백엔드 서버 측면에서는 아무런 예고도 없이 메인 프로세스(예: MySQL Daemon)가 갑자기 증발해 버리는 최악의 재앙으로 변모하게 됩니다. DBA 엔지니어들은 이 학살에서 데이터베이스를 보호하기 위해 특정 프로세스의 oom_score_adj 파라미터를 인위적으로 -1000 점으로 고정시켜 OOM Killer의 레이더망에서 완전히 빗나가도록 영구 면책 특권을 부여하거나, 아예 커널 파라미터(vm.overcommit_memory)를 보수적으로 수정해 시스템이 소유한 물리 메모리와 스왑 용량의 한계를 한 바이트라도 넘어서는 순간 malloc 자체를 거부당하게 만드는 고전적이고 확실한 안전장치를 최우선적으로 구축하기도 합니다.