자바는 GC에 의해 오랜 시간동안 참조되지 않은 메모리가 회수 된다
이런 메모리 관리에 의해 우리는 new/delete, malloc/free
를 사용하지 않고 편하게 프로그래밍을 할 수 있다
하지만 GC가 메모리를 회수하지 못하는 경우, 혹은 GC 자체에 문제가 생기는 경우가 있는데 이때 Memory Leak(메모리 누수)가 발생하고 지속될시엔 OOM(Out Of Memory)나
메모리를 할당할 수 없는 Can not allocate memory
가 발생할 수 있다
이런 일이 발생할 시 애플리케이션은 그대로 멈추거나 종료되므로 대응을 할 필요가 있다
리눅스에서는 가상 메모리를 스왑 메모리 시스템이라 호칭하는데
기본적으로 CentOS에는 스왑 메모리리의 파티셔닝 설정이 되어있지 않다
실제 메모리(RAM)가 아닌 저장소(SSD/HDD)를 사용하기 때문에 속도가 훨씬 느리지만 메모리 부족이 발생해도 자동으로 스왑 메모리로 확장되며 서비스 중단을 피할 수 있다는 점에서 적절한 사용이 필요하다
이런 스왑 메모리의 미설정으로 인해 JVM의 메모리를 할당할 수 없는 에러가 발생했었다
OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000e0800000, 130023424, 0) failed; error='Cannot allocate memory' (errno=12)
프로젝트를 자동 배포하다보니 서버를 계속 재배포하여 발생한 에러로
발생 원인은
aws t2.micro의 경우 1GB 램을 사용하는데 톰캣이 빌드될 때마다 스레드 커넥션이 일어나고 이걸 Full GC하기 까지 시간이 소모되어 메모리 누수가 발생, 메모리 초과되었다
따라서 이 때는 기본적으로 설정되지 않은 스왑 메모리를 t2.micro 물리 메모리의 2배인 2GB를 줘서 해결했다
스레드 커넥션으로 인한 메모리 누수는 확인이 되었는데, 아직 참조유지에 의한 메모리 누수가 있다
자바에서 메모리는 각 구역을 거쳐 최종적으로 Old Generation에서 회수되는데 이 때
아직 참조유지에 의해 객체가 살아있을 경우 객체는 계속해서 Old 영역에 쌓이게 되고, 이걸 회수하기 위해 Major GC가 반복되다 결국 OOM 에러를 내게 된다
따라서 이러한 참조유지에 의한 메모리 릭을 파악하고 해결하는 것이 필요하다
메모리 릭의 확인은 보통 GC 자체의 문제보다는 개발자의 실수로 발생하는 일이 잦기 때문에
객체 중 어떤 객체가 메모리 릭을 발생시키는지 파악할 수 있어야 한다
이러한 객체의 메모리 소유는 일반적인 메모리 점유율로는 파악할 수 없어 Heap-Dump라는 방법을 사용한다
힙 덤프에 대해서는 아직 이정도까지 접근하기엔 조금 먼 이야기라 힙 덤프를 어떤 방식으로 할 수 있는지에 대한 링크만 기록한다
우아한 형제들 메모리 릭 이야기
http://honeymon.io/tech/2019/05/30/java-memory-leak-analysis.html
https://blog.jiniworld.me/33
https://dzone.com/articles/memory-leak-andjava-code