본문 바로가기
프로그래밍/Java & Spring

Java Spring OOM 분석 및 해결 후기

by 방구석개발자 2022. 8. 18.
반응형

최근 회사 어플리케이션을 배포 후 몇시간안에 다운되는 현상이 있었고 OOM문제가 생긴걸 알게 되었습니다.

OOM을 어떻게 분석하고 해결했는지 정리하고자 합니다.

원인

원인은 간단했습니다.

저희 서비스는 자바11버전을 사용하고 있었습니다.

ForkJoinPool을 사용하는 로직을 새로 배포하였는데요.

자바8과 자바11에서 ForkJoinPool이 내부적으로 차이가 있었고 gc에서 메모리 해제하는 시간이 느려져 OOM이 이슈가 발생된 것이였습니다.

https://meetup.toast.com/posts/291 을 참고해서 원인을 파악했습니다.

해결

ForkJoinPool을 지역변수로 사용하였지만 Service계층에서 생성자를 이용하여 한번만 객체 생성하고 해당 thread를 계속 재사용하는 방법으로 간단하게 해결하였습니다.

분석 및 해결 방법

처음 OOM jmap을 통한 분석을 하려고 했으나 운영환경에 비슷하게 맞추고 이미지 빌드하고 배포하고 확인하는 시간이 오래 걸리므로 

로컬에서 확인했습니다. 빠르게 피드백을 보는게 좋을것이라 생각되었기 때문입니다.

또 새로운 코드에서 문제가 생긴걸 알게 되어 문제되는 원인을 빠르게 찾을수 있을거라 생각도 있었습니다.

하여 인텔리제이 프로파일링 + k6 부하테스트를 이용하여 로컬에서 테스트를 진행했습니다.

k6 다운로드 및 설치

우선 k6를 다운 및 설치 합니다.

 

Installation

k6 has packages for Linux, Mac, and Windows. As alternatives, you can also using a Docker container or a standalone binary.

k6.io

brew install k6

k6 스크립트 작성

그 이후에 부하테스트에 필요한 스크립트를 작성합니다. 작성시에는 k6 docs를 참고 했습니다.

import http from 'k6/http';
import {check, group, sleep, fail} from 'k6';

export let options = {
    stages: [
        {duration: '5s', target: 50},
        {duration: '10s', target: 50},
        {duration: '5s', target: 100},
        {duration: '10s', target: 100},
        {duration: '5s', target: 150},
        {duration: '10s', target: 150},
    ],
    thresholds: {
        http_req_duration: ['p(99)<200'],
    },
};

const BASE_URL = 'http://localhost:8888';

const header = {
    headers: {
        authorization: '로그인에 필요한 jwt token'
    },
};

export default function () {
    let pathResponse = http.get(`${BASE_URL}/테스트에필요한url`
    ,header);

    check(pathResponse, {
        'find path': response => response.status === 200
    });

    sleep(1);
};

k6 실행

저장한 k6스크립트를 실행합니다.

k6 run test.js

k6 결과 확인

k6실행 후에는 아래와 같이 결과값을 확인 할 수 있습니다.

running (0m46.0s), 000/150 VUs, 612 complete and 0 interrupted iterations
default ✓ [======================================] 000/150 VUs  45s

     ✗ find path
      ↳  6% — ✓ 40 / ✗ 572

     checks.........................: 6.53%  ✓ 40        ✗ 572
     data_received..................: 37 kB  802 B/s
     data_sent......................: 136 kB 2.9 kB/s
     http_req_blocked...............: avg=267.04µs min=0s    med=0s     max=7.56ms   p(90)=804.9µs p(95)=1.01ms
     http_req_connecting............: avg=159.57µs min=0s    med=0s     max=3.48ms   p(90)=494.7µs p(95)=663.35µs
   ✗ http_req_duration..............: avg=5.9s     min=0s    med=0s     max=38.27s   p(90)=24.74s  p(95)=28.45s
       { expected_response:true }...: avg=22.89s   min=8.37s med=23.63s max=36.09s   p(90)=33.92s  p(95)=35.6s
     http_req_failed................: 93.46% ✓ 572       ✗ 40
     http_req_receiving.............: avg=4.77ms   min=0s    med=0s     max=460.31ms p(90)=0s      p(95)=7.51ms
     http_req_sending...............: avg=1.04s    min=0s    med=0s     max=32.16s   p(90)=205.8µs p(95)=5.78s
     http_req_tls_handshaking.......: avg=0s       min=0s    med=0s     max=0s       p(90)=0s      p(95)=0s
     http_req_waiting...............: avg=4.85s    min=0s    med=0s     max=38.27s   p(90)=23.74s  p(95)=26.39s
     http_reqs......................: 612    13.307262/s
     iteration_duration.............: avg=6.91s    min=1s    med=1s     max=39.28s   p(90)=25.74s  p(95)=29.46s
     iterations.....................: 612    13.307262/s
     vus............................: 37     min=10      max=150
     vus_max........................: 150    min=150     max=150

ERRO[0047] some thresholds have failed

인텔리제이 프로파일러 실시간 확인

1. 인텔리제이에서 스프링부트를 실행 합니다.

2. 프로파일러탭을 눌러서 실행된 어플리케이션을 클릭합니다. Cpu and Memory Live Charts를 누릅니다.

3. 실시간으로 cpu,heap memory, threads, non-heap memory를 확인합니다.

부하테스트 후 Charts를 확인합니다.

개선 전

부하 테스트 3번 실행 후 캡쳐(개선 전)

개선 후

부하 테스트 3번 실행 후 캡쳐(개선 후)

이전보다 메모리도 많이 올라가지 않고 thread수도 안정적인 모습을 볼 수 있었습니다.

다음번에는

만약 OOM문제가 다시 생긴다면 최대한 운영 환경에 맞게 맞추고 분석을 진행해보고 싶습니다.

다행이 이번엔 로컬에서 작업해서 마무리 할 수 있었습니다. 하지만 자바8에서 자바11로 버전을 올렸을때도 같은 이슈가 생길것이고 이번엔 원인이 어디에 생겼는지도 모른채 분석을 해야되기 때문입니다.

힙덤프 뜨는 방법과 다양한 부하테스트를 먼저 학습해보자.

분석하는 시간보다 부하테스트툴과 힙덤프를 어떻게 뜨는지 어떤 툴로 사용하는지 알아보는 시간이 더 길었습니다.

만약에 미리 알고 있었더라면 더 빠르게 해결 가능하므로 미리 학습을 하겠습니다.

반응형

댓글