어제 학습한 것처럼, Major GC(Full GC)는 "Stop-the-World"를 유발하여 애플리-케이션의 응답성에 큰 영향을 줍니다. JVM 튜닝의 가장 기본적인 목표는 애플리-케이션의 특성에 맞게 힙(Heap) 메모리 크기를 조절하여, Full GC의 발생 빈도를 줄이고 실행 시간을 최소화하는 것입니다.
핵심 원리:
-D나 -X 플래그를 통해 전달합니다.| 옵션 | 설명 | 예시 |
|---|---|---|
-Xms<size> | JVM 시작 시 할당받는 최소 힙 메모리 크기 (Initial Heap Size) | -Xms256m (256MB) |
-Xmx<size> | JVM이 사용할 수 있는 최대 힙 메모리 크기 (Maximum Heap Size) | -Xmx1024m (1GB) |
-Xmn<size> | Young Generation의 크기를 지정합니다. | -Xmn128m (128MB) |
-Xms와 -Xmx를 동일한 값으로 설정하는 것이 일반적입니다. 이는 런타임 중에 JVM이 힙 크기를 동적으로 조절(resizing)하면서 발생하는 불필요한 오버헤드와 Full GC를 방지하기 위함입니다.java -Xms512m -Xmx512m -jar my-app.jar문제점: 데이터베이스에 접근해야 할 때마다 매번 커넥션을 새로 생성(TCP/IP 연결, DB 인증 등)하고, 작업이 끝나면 해제하는 것은 매우 비용이 비싼 작업입니다. 동시 요청이 많은 웹 애플리-케이션에서는 이 과정이 성능의 심각한 병목이 됩니다.
커넥션 풀: 애플리-케이션이 시작될 때, 미리 일정 개수의 데이터베이스 커넥션을 생성하여 "풀(Pool)"에 보관해두고, 필요할 때마다 이 풀에서 커넥션을 빌려 쓰고, 사용이 끝나면 다시 반납하는 기술입니다.
application.yml에서 다음과 같은 주요 속성을 튜닝하여 성능을 최적화할 수 있습니다.| 속성 | 설명 | 권장 사항 |
|---|---|---|
maximum-pool-size | 커넥션 풀이 가질 수 있는 최대 커넥션 개수. | (코어 수 * 2) + (스핀들 수) 공식이 있지만, 부하 테스트를 통해 최적값을 찾아야 함. 너무 크면 오히려 DB에 부하. |
minimum-idle | 풀에서 유지하는 최소 유휴(idle) 커넥션 개수. | maximum-pool-size와 동일하게 설정하여, 런타임 중 커넥션 생성/제거 오버헤드를 줄이는 것이 좋음. |
connection-timeout | 풀에서 커넥션을 얻기 위해 대기하는 최대 시간 (밀리초). | 너무 길면 사용자가 오래 기다리게 됨. (e.g., 30000ms) |
문제점: 클라이언트로부터 요청이 들어올 때마다 매번 새로운 스레드를 생성하는 것은, 스레드 생성 및 컨텍스트 스위칭 비용 때문에 비효율적입니다.
내장 웹 서버의 스레드 풀: Spring Boot의 내장 웹 서버(Tomcat 등)는 스레드 풀을 사용하여 이 문제를 해결합니다. 미리 일정 개수의 스레드를 생성해두고, 요청이 들어오면 풀에 있는 스레드를 할당하여 작업을 처리하고, 작업이 끝나면 스레드를 다시 풀에 반납합니다.
application.yml에서 내장 Tomcat의 스레드 풀 동작을 제어할 수 있습니다.| 속성 | 설명 |
|---|---|
server.tomcat.threads.max | 스레드 풀이 가질 수 있는 최대 스레드 개수. (기본값: 200) 이 개수를 초과하는 요청은 큐에서 대기. |
server.tomcat.threads.min-spare | 항상 활성 상태로 유지하는 최소 스레드 개수. (기본값: 10) |
server.tomcat.accept-count | 최대 스레드가 모두 사용 중일 때, 들어오는 연결 요청을 대기 큐(Queue)에 쌓아둘 수 있는 최대 개수. (기본값: 100) |
max-threads를 무작정 늘리는 것이 능사는 아닙니다. CPU 코어 수, I/O 작업의 비중, 외부 시스템의 응답 시간 등을 고려하여, 부하 테스트(nGrinder)를 통해 최적의 값을 찾아야 합니다.문제점: System.out.println()을 사용하여 로그를 출력하는 것은 여러 단점이 있습니다.
SLF4J와 Logback: Spring Boot는 기본적으로 SLF4J(Simple Logging Facade for Java)를 로깅 추상체(Facade)로, Logback을 구현체로 사용합니다.
로그 레벨 설정:
application-prod.yml에서 기본 로그 레벨을 INFO로 설정하여, 불필요한 DEBUG 로그가 운영 환경의 성능에 영향을 주지 않도록 합니다.DEBUG 레벨을 활성화하여 문제 추적을 용이하게 할 수 있습니다.비동기 로거 (Async Appender):
AsyncAppender를 사용하면, 로그 이벤트를 별도의 큐(Queue)에 저장하고, 다른 스레드가 이 큐에서 로그를 가져와 파일에 쓰는 비동기 방식으로 동작합니다.-Xms와 -Xmx 옵션을 동일하게 설정하여 불필요한 Full GC를 방지하는 것입니다.INFO로 조정하고, 비동기 로거를 사용하여 로깅으로 인한 성능 저하를 최소화해야 합니다.