4 분 소요


상용 PDF 솔루션(PDFC) 메모리 문제 분석 및 운영 안정화 경험

들어가며

회사에서는 PDF 생성 기능을 위해 PDFC라는 상용 솔루션을 사용하고 있었다.

PDFC는 사내에서 개발한 프로그램이 아니라 외부에서 제공하는 상용 제품이었기 때문에 소스코드를 수정하거나 내부 동작을 변경할 수 없는 환경이었다.

어느 날부터 PDF 생성 서버에서 이상한 현상이 발생하기 시작했다.

  • PDF 생성 속도가 점점 느려짐
  • 간헐적인 PDF 생성 실패

처음에는 단순한 서버 문제라고 생각했지만, 로그를 분석해보니 생각보다 복잡한 문제였다.

이번 글에서는 장애 발생부터 원인 추적, 그리고 운영 안정화까지의 과정을 정리해보려고 한다.


문제 발생

운영 서버를 모니터링하던 중 Java 프로세스가 비정상적으로 많은 메모리를 사용하고 있는 것을 확인했다.

java.exe 2GB 이상 사용

특히 PDF 생성 요청이 많아질수록 메모리 사용량이 계속 증가했다.

더 큰 문제는 시간이 지나도 메모리가 충분히 줄어들지 않는다는 점이었다.


오류 로그 확인

먼저 서버 로그를 확인했다.

siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), reading address 0x0000000000000419

해당 오류는 프로그램이 접근할 수 없는 메모리 영역에 접근하려고 할 때 발생한다.

챗 gpt가 일반적으로 다음과 같은 상황에서 발생할 수 있다고 한다.

  • 잘못된 메모리 참조
  • 메모리 손상
  • JVM 메모리 부족
  • 네이티브 라이브러리 문제

로그를 추가로 살펴보던 중 JVM 옵션도 확인할 수 있었다.

-XX:+HeapDumpOnOutOfMemoryError

이 옵션은 JVM에서 OutOfMemoryError가 발생했을 때 Heap Dump를 생성하는 옵션이다.

즉, 메모리 관련 문제가 발생할 가능성을 이미 고려하고 있었던 것으로 보였다.


JVM 메모리 상태 확인

JVM 설정 정보도 함께 확인했다.

InitialHeapSize = 134217728
MaxHeapSize     = 2147483648

이를 사람이 읽기 쉬운 단위로 변환하면 다음과 같다.

항목 크기
InitialHeapSize 약 128MB
MaxHeapSize 약 2GB

PDF 서버는 특성상 메모리 사용량이 많다.

PDF를 생성하는 과정에서

  • PDF 문서 객체
  • 이미지 데이터
  • 렌더링 버퍼
  • 변환 결과

등이 모두 JVM Heap에 생성된다.

따라서 처음에는 단순히 Heap 크기가 부족한 문제일 수도 있다고 생각했다.

하지만 여기서 한 가지 제약이 있었다.

PDFC는 상용 솔루션이었기 때문에 내부 코드를 분석하거나 수정할 수 없었다.

결국 로그 분석을 통해 원인을 추적해야 했다.


GC 로그 수집

메모리 상태를 정확히 확인하기 위해 GC 로그를 활성화했다.

-Xlog:gc*:C:\temp\gc-%t.log

GC 로그를 수집한 이유는 다음과 같다.

  • GC가 정상적으로 수행되는지 확인
  • 어떤 영역에 메모리가 남아있는지 확인
  • 메모리 누적 여부 확인

GC는 정상적으로 동작하고 있었다

로그를 분석하던 중 다음 내용을 발견했다.

GC(6792) Pause Young (Normal)
3708M -> 2241M (4077M)

의미는 다음과 같다.

GC 전 메모리 사용량 : 3708MB
GC 후 메모리 사용량 : 2241MB

즉 약 1.4GB 정도의 메모리를 회수한 것이다.

이 로그만 보면 GC는 정상적으로 동작하고 있었다.

처음 예상했던 “GC가 아예 동작하지 않는다”는 가설은 틀린 것으로 보였다.


이상한 점 발견

조금 더 자세히 살펴보니 이상한 로그가 눈에 들어왔다.

Old regions: 1585 -> 1585

GC 전

1585

GC 후

1585

변화가 없었다.


Old Region은 왜 중요할까?

G1 GC는 Heap 영역을 여러 개의 Region으로 나누어 관리한다.

대표적으로 다음과 같은 영역이 존재한다.

  • Eden Region
  • Survivor Region
  • Old Region
  • Humongous Region

이 중 Old Region은 오래 살아남은 객체들이 저장되는 공간이다.

즉,

  • 캐시 데이터
  • 이미지 객체
  • PDF 관련 객체
  • 장기 생존 객체

등이 저장된다.

GC를 수행했는데도 Old Region이 줄어들지 않는다는 것은

“오래된 객체들이 계속 메모리에 남아 있다”

는 의미였다.


두 번째 단서, Humongous Allocation

로그를 계속 분석하던 중 다음 메시지가 반복적으로 나타났다.

G1 Humongous Allocation

처음에는 큰 의미가 없다고 생각했지만 사실 이 로그가 가장 중요한 단서였다.


Humongous Object란?

G1 GC에서는 매우 큰 객체를 Humongous Object라고 부른다.

대표적으로

  • 고해상도 이미지
  • 대용량 배열
  • 렌더링 버퍼

등이 해당될 수 있다.

PDF 생성 과정에서는 이런 객체가 자주 발생할 가능성이 높다.


실제 로그 분석

다음 로그를 확인해보자.

Humongous regions: 607 -> 605

GC 전

607개

GC 후

605개

단 2개만 회수되었다.

즉 대부분의 대형 객체가 여전히 메모리에 남아있다는 의미였다.


원인 추정

분석 결과 다음과 같은 가능성을 의심하게 되었다.

  • PDF 처리 과정에서 생성된 객체 누적
  • 이미지 버퍼 누적
  • 내부 캐시 누적
  • 메모리 누수

다만 PDFC는 상용 솔루션이기 때문에 실제 어떤 객체가 남아있는지 확인하려면 제조사 수준의 분석이 필요했다.

운영 담당자로서는 GC 로그를 통해 “메모리가 지속적으로 누적되고 있다”는 사실까지만 확인할 수 있었다.


현실적인 해결 방법

문제는 원인을 찾는 것보다 서비스를 안정적으로 운영하는 것이었다.

상용 솔루션 내부를 수정할 수 없는 상황에서 선택할 수 있는 방법은 많지 않았다.

그래서 운영 관점에서 접근하기로 했다.


JAVA_TOOL_OPTIONS 활용

따라서 JVM이 시작될 때 자동으로 읽는 환경변수인 JAVA_TOOL_OPTIONS를 활용하여 설정을 적용하였다.

-Xms4G -Xmx4G -XX:G1HeapRegionSize=4M -Xlog:gc*:C:\temp\gc-%t.log:time:

각 옵션의 의미는 다음과 같다.

옵션 설명 -Xms4G JVM 시작 시 Heap 메모리를 4GB로 할당 -Xmx4G JVM이 사용할 수 있는 최대 Heap 메모리를 4GB로 제한 -XX=4M G1 GC의 Region 크기를 4MB로 설정

JAVA_TOOL_OPTIONS는 JVM 실행 명령을 직접 수정할 수 없는 환경에서도 JVM 옵션을 전달할 수 있는 특수 환경변수이다.

상용 솔루션이나 Windows 서비스 형태의 애플리케이션에서 JVM 설정을 적용해야 할 때 자주 사용된다.

이번 사례에서도 PDFC 내부 실행 구조를 수정할 수 없었기 때문에 환경변수를 통해 JVM 설정을 적용하였다.

또한 GC 동작을 분석하기 위해 GC 로그 수집도 함께 구성하였다.

-Xlog:gc*:C:\temp\gc-%t.log:time


서비스 자동 재시작 적용

Windows 작업 스케줄러를 이용하여 PDFC 서비스를 주기적으로 재시작하도록 구성했다.

서비스가 재시작되면 JVM도 함께 재시작된다.

결과적으로

  • Heap 메모리 초기화
  • 누적 객체 제거
  • 장시간 운영으로 인한 메모리 증가 현상 완화

효과를 얻을 수 있었다.


결과

적용 이후 다음과 같은 변화를 확인할 수 있었다.

  • PDF 생성 실패 감소
  • 장시간 운영 시 장애 감소
  • 메모리 사용량 안정화
  • 서버 응답 속도 개선

근본 원인을 제거한 것은 아니지만 운영 환경에서는 충분히 효과적인 대응이었다.


마무리

이번 경험을 통해 알게 된 점은 다음과 같다.

  • GC가 실행된다고 해서 메모리가 모두 회수되는 것은 아니다.
  • Old Region과 Humongous Region은 중요한 분석 포인트다.
  • 상용 솔루션은 문제를 발견해도 직접 수정할 수 없는 경우가 많다.
  • 운영 환경에서는 “근본 원인 해결”보다 “서비스 안정화”가 우선일 때가 있다.
  • 로그 분석 능력은 운영 환경에서 매우 중요하다.

이번 사례는 단순한 메모리 부족 문제가 아니라, 상용 PDF 솔루션의 메모리 누적 현상을 GC 로그를 통해 분석하고 운영 관점에서 안정화한 경험이었다.

댓글남기기