문제 인식
운영 중인 EC2 인스턴스(t3.large)에서 CloudWatch Agent 기반 메모리 모니터링 중 82.4% 의 높은 메모리 사용률을 확인했습니다.
DB 서버도 아닌 애플리케이션 서버에서 이 정도 메모리 사용률은 비정상적이었고, 이 상태가 지속되면 메모리 누수나 스왑 의존으로 인한 성능 저하가 우려되었습니다.

서버 사양 확인
| 항목 | 값 |
|---|---|
| 인스턴스 타입 | t3.large |
| vCPU | 2개 |
| 메모리 | 8 GiB |
| 네트워크 성능 | Moderate |
| EBS 최적화 | 지원 (최대 2,250 Mbps) |
| CPU 모델 | 크레딧 기반 버스트 |
t3.large는 간헐적 부하에 적합한 버스트 가능 인스턴스로, 웹 서버나 마이크로서비스 운영에 주로 사용됩니다. 8GB 메모리에서 82%를 상시 점유하는 것은 정상적인 운영 패턴이 아니었습니다.
메모리 상태 분석 (free -m)

$ free -m
total used free shared buff/cache available
Mem: 7719 6359 142 64 1216 996
Swap: 10239 1719 8520물리 메모리
| 항목 | 값 | 비고 |
|---|---|---|
| Total | 7,719 MB (~7.7 GB) | |
| Used | 6,359 MB (~6.4 GB) | 앱 + 캐시/버퍼 포함 |
| Free | 142 MB (~0.14 GB) | 실질적으로 여유 없음 |
| Buff/Cache | 1,216 MB (~1.2 GB) | Linux 캐시 (필요 시 해제 가능) |
| Available | 996 MB (~1 GB) | 실제 사용 가능한 메모리 |
스왑 메모리
| 항목 | 값 |
|---|---|
| Total | 10,239 MB (~10.2 GB) |
| Used | 1,719 MB (~1.7 GB) |
| Free | 8,520 MB (~8.5 GB) |
스왑 사용량이 1.7GB에 달한다는 것은 물리 메모리가 충분하지 않아 디스크 기반 스왑에 의존하고 있다는 의미입니다. 스왑 I/O는 물리 메모리 대비 수십~수백 배 느리므로 성능 저하의 직접적 원인이 됩니다.
프로세스별 메모리 점유 분석 (top / ps aux)
top 명령어와 ps aux | grep java 를 통해 메모리를 많이 사용하는 프로세스를 식별했습니다.

주요 프로세스 목록
| 순위 | PID | 프로세스 | 메모리 사용률 | CPU 사용률 | 메모리 크기 |
|---|---|---|---|---|---|
| 1 | 13596 | Java App (메인 서비스) | 47.9% | 14.0% | ~3.6 GB |
| 2 | 16314 | Java App (Java 1.8) | 9.7% | - | - |
| 3 | 25611 | Java App (에이전트) | 7.8% | 0.3% | ~616 MB |
| 4 | 7331 | Partner Agent (OpenJDK 18) | 3.3% | - | - |
| 5 | 8889 | Proxy Service (OpenJDK 18) | 2.7% | - | - |
| 6 | 2748 | CloudWatch Agent | 0.4% | 0.7% | ~31 MB |
핵심 원인: PID 13596 Java 프로세스 하나가 시스템 메모리의 절반(47.9%) 을 차지하고 있었습니다. 여러 Java 프로세스가 동시에 실행되면서 총 메모리 사용률이 82%를 넘긴 상황이었습니다.
PID 16314 실행 파일 확인
$ sudo ls -l /proc/16314/exe
lrwxrwxrwx 1 root root 0 Oct 14 00:34 /proc/16314/exe -> /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.322.b06-1.el7_9.x86_64/jre/bin/java
해당 프로세스는 시스템에 설치된 OpenJDK 1.8 런타임으로 실행된 Java 프로세스였습니다.
나머지 1GB의 행방 (cat /proc/meminfo)
Java 프로세스들의 합산 메모리와 buff/cache를 제외하고도 약 1GB가 어디에 사용되는지 확인했습니다.
cat /proc/meminfo 분석 결과:
- 커널 메모리(Slab): 커널 내부 데이터 구조 캐싱
- Inactive 메모리: 최근 사용되지 않았지만 아직 해제되지 않은 페이지
- 시스템 관련 메모리 관리 오버헤드
이 1GB는 커널과 시스템 레벨에서 사용하는 메모리로, 애플리케이션이 직접 제어할 수 없는 영역입니다.
모니터링 추이
초기 확인 시 82.4% 였던 메모리 사용률이 며칠 후 86.6% 로 상승했습니다.

일주일간 평균 메모리 사용량은 약 81.7%, 스왑 사용량은 1.7GB/10.2GB 수준을 유지했습니다.
메모리 사용률이 지속적으로 우상향하는 패턴은 메모리 누수 가능성을 시사합니다.
분석 결과 및 대응 방향
원인 요약
- 다수의 Java 프로세스 동시 실행: 메인 서비스(47.9%) + 에이전트(7.8%) + 기타 Java 프로세스들이 8GB 메모리를 거의 소진
- 스왑 의존: 물리 메모리 부족으로 1.7GB 스왑 사용 중 → 디스크 I/O 기반이므로 성능 저하 유발
- Available 메모리 1GB 미만: 새로운 프로세스 실행이나 부하 증가 시 OOM 위험
대응 방안
- JVM Heap/Non-Heap 메모리 모니터링 도입: 각 Java 프로세스의
-Xmx,-Xms설정 확인 및 적정 값 조정 - 불필요한 Java 프로세스 정리: 동일 서버에서 실행 중인 프로세스 중 통합 가능한 것 검토
- 인스턴스 타입 업그레이드 검토: t3.large(8GB) → t3.xlarge(16GB) 등
- 메모리 사용률 알람 설정: CloudWatch 기반 80% / 90% 임계치 알람 구성
- 주기적 메모리 트렌드 리포트: 메모리 누수 여부 장기 추적
마무리
단순히 "메모리 사용률이 높다"에서 끝나지 않고, free -m → top → ps aux → /proc/meminfo 순서로 단계적으로 원인을 추적했습니다.
인프라 운영에서 메모리 이슈는 즉각적인 장애로 이어지지 않더라도, 스왑 의존 → 성능 저하 → OOM Kill 순서로 점진적으로 악화되기 때문에 조기 발견과 대응이 중요합니다.