백엔드트러블슈팅

[GCP] Java 서버 OOM으로 계속 죽는 문제, Swap 메모리로 해결한 이야기

김동현
··2분 읽기

GCP e2-micro 인스턴스에서 Spring Boot 서버가 OOM으로 계속 종료되는 문제를 Swap 메모리 설정으로 해결한 과정을 기록

POP-SPOT를 GCP e2-micro 인스턴스(RAM 1GB)에 배포한 후 서버가 하루에 2~3번씩 그냥 죽어버리는 현상이 생겼다.

처음엔 코드 문제인 줄 알고 로그 한참 뒤졌는데...

에러 메시지

javascript
java.lang.OutOfMemoryError: Java heap space
    at java.base/java.util.Arrays.copyOf(Arrays.java:3512)
    ...
Killed

OOM(Out Of Memory) 에러임. 서버가 사용할 수 있는 메모리가 꽉 찼을 때 운영체제가 강제로 프로세스를 종료하는 현상.

쉽게 말하면, 방(메모리)이 꽉 찼는데 짐을 더 넣으려 하면 집주인(OS)이 세입자(서버 프로그램)를 강제로 내쫓는 것과 같음.

왜 그랬냐면

e2-micro 인스턴스 스펙은 RAM 1GB. 여기서 OS, Redis, 기타 프로세스가 메모리를 나눠 씀.

Spring Boot가 쓸 수 있는 기본 메모리는 전체 RAM의 약 25%, 즉 256MB 정도. 나머지는 OS나 다른 프로그램이 씀.

RAM 1GB를 4명이 나눠 쓰는 고시원 같은 상황. Spring Boot한테 돌아오는 방이 너무 좁음.

특히 POP-SPOT은 AI 기능(LangChain4j)이랑 실시간 채팅(WebSocket)을 동시에 처리하다 보니 일반 API 서버보다 메모리를 더 많이 잡아먹음. 결국 방이 터진 것.


해결 — Swap 메모리 설정

Swap 메모리란?
RAM이 꽉 찼을 때 하드디스크(SSD)의 일부 공간을 임시 메모리처럼 쓰는 기술. 속도는 RAM보다 훨씬 느리지만 OOM으로 서버가 죽는 건 막을 수 있음.
컴퓨터 책상(RAM)이 꽉 차면 옆 바닥(디스크)에 잠깐 올려두는 것과 같음.

먼저 현재 상태 확인.

bash
free -h
javascript
              total        used        free
Mem:           985M        820M        165M
Swap:            0B          0B          0B

Swap이 0인 것을 확인. 2GB로 설정함.

bash
# 1. 2GB 크기의 Swap 파일 생성
#    (디스크에 2GB짜리 임시 메모리 공간을 만드는 것)
sudo fallocate -l 2G /swapfile

# 2. 보안 권한 설정 (root 계정만 접근 가능하게)
sudo chmod 600 /swapfile

# 3. Swap 형식으로 포맷
sudo mkswap /swapfile

# 4. Swap 활성화
sudo swapon /swapfile

# 5. 서버 재부팅 후에도 자동으로 켜지도록 등록
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

설정 후 다시 확인.

bash
free -h
javascript
              total        used        free
Mem:           985M        820M        165M
Swap:          2.0G         0B        2.0G

Swap 2GB가 추가된 것을 확인.


결과


한계

Swap은 완벽한 해결책이 아님. 디스크를 메모리처럼 쓰기 때문에 속도가 느리고, Swap을 많이 쓸수록 서버 전체가 느려질 수 있음.

근본적인 해결책은 두 가지.

  • 인스턴스 스펙 업그레이드 (e2-micro → e2-small 등)
  • JVM 메모리 튜닝 (-Xmx, -Xms 옵션으로 힙 크기 직접 지정)
  • 다만 무료 티어나 저사양 서버를 유지해야 하는 상황에서는 Swap 설정이 가장 빠르고 효과적인 방법. GCP e2-micro에 Spring Boot 올릴 거면 배포 전에 Swap부터 잡고 시작하는 게 맞음.