BIEE 10g 메모리/CPU 급증 및 Hang 이슈: 시작부터 종료까지

BIEE 10g 메모리 CPU 급증 및 Hang 이슈
BIEE 10g CSV로 OOM·GC 정지 발생: 원인과 즉시 조치
📅 2025-08-29· OBIEE10gOC4JJDK1.5 CSVGCBI_Publisher성능_장애
⚡ 바로 적용할 조치 보기
한눈에 보기
업무가 가장 몰리는 오전 10시 30분 전후, 여러 건의 대용량 CSV 다운로드가 한꺼번에 겹치면서 JVM 메모리가 4.9GB 수준에서 최대치(6GB)에 가까워졌고, 그 과정에서 CPU는 짧게 50% 안팎으로 튀었습니다. 동일 구간에 OOM 메시지가 24회 연속 발생했고 네트워크 레이어에서는 “Broken pipe” 로그가 차례로 찍혔습니다. JDK 1.5 시절의 STW(Stop-The-World) 중심 GC가 적용된 상태에서 가드레일과 스풀 제한이 비어 있어 대형 결과셋이 Old 영역을 가득 채운 것이 핵심 흐름입니다. 우선 가드레일·스풀·야간화·GC 로깅을 묶어 적용한 뒤 동일 시간대 로그로 전·후를 비교했고, 추가로 Parallel GC 보완과 힙(6→4GB) 축소는 조건 충족 시 A/B로 검토했습니다.
이 글은 먼저 배경과 환경을 간단히 짚고, 실제로 문제가 진행된 타임라인을 설명합니다. 이어서 수집된 데이터에서 무엇을 관찰했는지 정리한 뒤, 원인(RCA)을 자연스럽게 연결합니다. 그 다음으로 현장에서 바로 적용할 수 있는 즉시 조치 방법을 구체적인 설정 스니펫과 함께 제시하고, 조치의 효과를 어떻게 검증할지, 안정화 이후에 고려할 선택 과제, 마지막으로 리스크와 롤백 계획까지 순서대로 안내합니다.

1. 범위와 배경

대상 시스템은 BIEE 10g 환경으로, Presentation/OC4J 구성 위에 JDK 1.5.0_18(64-bit, sparcv9) 서버 VM이 올라가 있습니다. 사용자 체감으로는 응답이 점점 늦어지다가 어느 순간 멈춘 듯 보이는 구간이 반복되었고, 운영팀에서는 메모리와 CPU가 동시에 뛰는 패턴을 보고했습니다. 이번 글의 목표는 원인을 명확히 규명하고, 즉시 안정화를 이룬 다음, 로그 근거에 기반한 단계적 개선 순으로 정리하는 것입니다.

2. 환경 개요

핵심 경로는 세 가지를 봤습니다. 우선 웹 쪽은 $ORACLE_HOME/data/web/config/instanceconfig.xml에 프레젠테이션 설정이 모여 있고, 스케줄러는 $ORACLE_HOME/data/scheduler/config/instanceconfig.xml로 따로 분리되어 있습니다. BI Publisher 설정은 $JAVA_HOME/jre/lib/xdo.cfg$ORACLE_HOME/data/web/config/xdo.cfg 두 곳을 함께 확인해야 현행이 정확히 드러납니다. GC 로그 플래그는 OC4J 기동 인자에서 확인했는데, CMS/ParNew 없이 기본 STW 위주로 운용 중이었습니다.

bashOC4J 기동 스냅샷
-Xms6144m -Xmx6144m -XX:NewSize=2048m -XX:MaxNewSize=2048m -XX:SurvivorRatio=3
-XX:MaxPermSize=512m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps \
-Xloggc:/u01b/.../oc4j_gc.log
# (UseConcMarkSweepGC / UseParNewGC 부재)

3. 타임라인

10시 20분부터 10시 29분까지는 JVM RSS가 약 4.9GB 수준에서 안정적으로 유지되었습니다. 10시 30분을 지나면서 대량의 CSV 요청이 동시에 들어와 RSS가 상단을 향해 치고 올랐고, 같은 시점에 Java 프로세스 CPU도 50% 안팎으로 짧게 튀었습니다. 10시 37분부터 11시 17분 사이에는 OOM 메시지가 24회 연속으로 남았고, 그 사이사이에 100MB가 넘는 CSV 시도가 여럿 포착되었습니다.

4. 수집 데이터와 관측

메모리는 힙 상한인 6GB에 근접했고, 동시 CSV 스트리밍과 단건 대형 결과가 섞이면서 GB 단위 버퍼가 순간적으로 필요해지는 구간이 만들어졌습니다. 프레젠테이션 계층에는 <MaxRows>, <MaxCells>, <InputStreamLimitInKB> 같은 가드레일이 비어 있었고, 스케줄러의 <MaxRowsTimesColumns>는 배치에만 영향을 줘 인터랙티브 CSV에는 적용되지 않았습니다. BI Publisher의 xdo.cfg는 스풀/상한이 빠져 있어 메모리 안에서만 처리하려는 성향이 강했습니다.

5. 문제 정의

업무 피크 시간에 대형 CSV 요청이 겹치면서 Old 영역이 급팽창했고, 그 결과 STW Full GC가 길게 반복되었습니다. 당시 JDK 1.5의 기본 GC 운용에 더해 프레젠테이션 가드레일과 BI Publisher 스풀 제한이 비어 있어, 순간적으로 큰 결과를 메모리에서만 감당하려던 것이 직접적인 촉발 요인이었습니다.

6. 기술적 인과관계(RCA)

사용자가 요청한 대형 결과셋이 프레젠테이션 계층으로 읽혀 스트리밍되는 동안, Young에서 Old로의 승격이 빠르게 누적되었습니다. 그 상태에서 STW 중심의 풀 GC가 반복되면서 응답이 길게 멈춰 보였고 CPU 역시 그에 맞춰 흔들렸습니다. 가드레일과 스풀 제한이 없으니 피크를 눌러줄 장치가 없었고, 결과적으로 장애 패턴이 쉽게 되살아날 수 있는 구조였습니다.

확인이 필요한 부분
당시 GC 로그가 충분히 남지 않아 힙의 정확한 피크와 실제 정지시간을 수치로 못 박지는 못했습니다. 동일 시간대에 GC 로깅을 활성화해 재수집한 뒤 다시 한 번 교차 검증하는 것이 안전합니다.

7. 즉시 조치(실행 가이드)

먼저 프레젠테이션 계층에 행·셀·입력 스트림 제한을 명시해 큰 결과를 통제하고, BI Publisher에는 메모리 한도를 설정한 뒤 반드시 디스크 스풀로 빠지도록 경로를 지정합니다. 대형 내보내기는 가급적 야간 스케줄러로 넘기고, 같은 시간대 비교를 위해 GC 로깅을 켭니다. 아래 설정을 그대로 적용하시면 됩니다.

파일: $ORACLE_HOME/data/web/config/instanceconfig.xml

xmlPresentation 가드레일
<ServerInstance>
  <AnswerDetail>
    <MaxRows>200000</MaxRows>
    <MaxCells>3000000</MaxCells>
  </AnswerDetail>
  <ODBC>
    <InputStreamLimitInKB>262144</InputStreamLimitInKB> <!-- 256MB -->
  </ODBC>
</ServerInstance>

경로: $JAVA_HOME/jre/lib/xdo.cfg, $ORACLE_HOME/data/web/config/xdo.cfg

xmlxdo.cfg
<properties>
  <property name="FOProcessingLimitInMemoryKB">262144</property>  <!-- 256MB -->
  <property name="system-temp-dir">/u01b/app/oracle/bi/10.1.3/data/tmp</property>
  <property name="fo-chunk-size">5000000</property>               <!-- 5MB -->
  <property name="fo-trim-leading-whitespaces">true</property>
</properties>
대형 내보내기는 야간 스케줄러에서 생성해 링크로 배포하는 편이 안전합니다. 필요 시 RPD에서도 쿼리 제한(예: Rows 20만, Time 600초)을 가볍게 걸어 피크 유입을 낮춥니다.
bashGC 로깅 플래그
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps \
-Xloggc:/u01b/.../oc4j_gc.log

8. 효과 검증

동일 시간대 기준으로 Full GC 횟수와 평균/최대 pause가 확실히 내려가면 1차 목표는 달성입니다. 256MB를 넘는 CSV는 차단되거나 경고가 떠야 하고, xdo 스풀 경로에는 중간 파일이 보이되 JVM RSS가 갑자기 치솟지 않는지 함께 확인합니다. 네트워크 레이어의 “Broken pipe” 빈도도 자연히 줄어드는 게 정상입니다.

bash검증용 grep/awk
# Full GC 횟수
egrep -c 'Full GC' /u01b/.../oc4j_gc.log

# pause 통계
egrep -o '[0-9]+\.[0-9]+ secs\]' /u01b/.../oc4j_gc.log | tr -d ']' \
| awk '{s+=$1; if($1>m)m=$1} END{printf "count=%d avg=%.3fs max=%.3fs\n", NR, s/NR, m}'

9. 안정화 이후 고려할 일

장기적으로는 힙을 유지한 채 Parallel GC + Adaptive를 가볍게 적용해 보거나, 모니터링 결과가 뒷받침되면 힙을 6GB에서 4GB로 줄여 A/B 테스트를 진행할 수 있습니다. 다만 Old가 3.2~3.5GB 이하로 안정되고, 승격 실패나 빈번한 Full GC가 보이지 않으며, 시스템 메모리 여유가 30% 이상인 조건에서만 시도하는 쪽이 안전합니다.

bashParallel + Adaptive
-XX:+UseParallelGC -XX:+UseAdaptiveSizePolicy -XX:ParallelGCThreads=<코어수> \
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps \
-Xloggc:/u01b/.../oc4j_gc.log

10. JavaHost 관련 메모

CSV 생성은 프레젠테이션(웹) 경로에서 이뤄지므로, JavaHost 힙은 이번 현상의 핵심 변수가 아닙니다. 같은 시간대에 보이는 EOFException은 세션 종료나 OOM 타이밍과 겹쳤을 가능성이 높습니다. 필요하면 pargs -l <PID> 또는 jinfo -flags <PID>-Xmx를 바로 확인하세요.

11. 리스크와 롤백 계획

가드레일과 스풀 제한을 적용하면 일부 대용량 내보내기가 차단될 수 있습니다. 업무상 불가피한 경우 예외 리스트를 운영하는 게 현실적입니다. GC 로깅으로 I/O가 약간 늘 수 있으니 logrotate로 크기를 관리하고, 모든 변경은 백업과 원복 스크립트를 먼저 준비한 뒤 진행합니다.

12. 오픈 이슈와 체크리스트

마무리로, 프레젠테이션 가드레일 적용과 재기동, 두 경로의 xdo.cfg 하드닝과 tmp 폴더 권한 점검, 야간 배치 전환 공지 및 링크 배포 동선 확정, GC 로깅 적용 후 동일 시간대 로그 수집, 안정화 이후 Parallel GC 보완 A/B, 조건 충족 시 6→4GB 축소 A/B 및 롤백 절차 점검까지 차례로 확인합니다.

부록 A) 운영/검증 명령

bash명령 모음
# 가드레일 존재 여부
grep -nE 'MaxRows|MaxCells|InputStreamLimitInKB' \
  "$ORACLE_HOME/data/web/config/instanceconfig.xml"

# xdo.cfg 핵심 키(두 경로 모두)
for f in "$JAVA_HOME/jre/lib/xdo.cfg" "$ORACLE_HOME/data/web/config/xdo.cfg"; do
  echo "== $f"
  grep -nE 'FOProcessingLimitInMemoryKB|system-temp-dir|fo-chunk-size' "$f"
done

# GC 모드/적응 정책
jinfo -flags <OC4J_PID> | egrep 'UseParallelGC|UseConc|UseParNew|UseAdaptiveSizePolicy'

# Full GC 횟수
egrep -c 'Full GC' /u01b/.../oc4j_gc.log

# pause 통계
egrep -o '[0-9]+\.[0-9]+ secs\]' /u01b/.../oc4j_gc.log | tr -d ']' \
| awk '{s+=$1; if($1>m)m=$1} END{printf "count=%d avg=%.3fs max=%.3fs\n", NR, s/NR, m}'

부록 B) 설정 스니펫

xmlPresentation 가드레일
<ServerInstance>
  <AnswerDetail>
    <MaxRows>200000</MaxRows>
    <MaxCells>3000000</MaxCells>
  </AnswerDetail>
  <ODBC>
    <InputStreamLimitInKB>262144</InputStreamLimitInKB>
  </ODBC>
</ServerInstance>
xmlBI Publisher
<properties>
  <property name="FOProcessingLimitInMemoryKB">262144</property>
  <property name="system-temp-dir">/u01b/app/oracle/bi/10.1.3/data/tmp</property>
  <property name="fo-chunk-size">5000000</property>
  <property name="fo-trim-leading-whitespaces">true</property>
</properties>
위로 스크롤