JAVA 가비지 컬렉션

25gStroy·2022년 5월 31일
0

JAVA

목록 보기
7/18

가비지 컬랙션이란?

가비지 컬렉션 튜닝은 자바 애플리케이션 성능을 개선하기 위해 할 수 있는 일 중 가장 중요합니다.

현재 JVM에서 사용할 수 있는 주요 가비지 컬렉터는 단일 CPU 머신에서 사용하는 시리얼 컬렉터(serial collector), 처리율 병렬 컬렉터(parallel collector), 동시 병렬 컬렉터(concurrent mark-sweep collector), G1(Garbage First) 컬렉터가 있습니다.

자바의 가장 매력적인 기능 중 하나는 개발자가 명시적으로 객체의 생명 주기를 관리할 필요가 없다는 점ㅣㅂ니다.
객체를 필요할때 생성하고 더이상 사용되지 않을 때 JVM이 자동으로 객체를 해체 시킵니다.

기본적으로 가비지컬렉터는 더이상 사용되지 않는 객체를 찾아서 그 객체와 관련된 메모리를 해체하도록 구성되어 있습니다.
JVM은 정기적으로 미사용 객체가 있는 힙을 통해서 찾습니다. 미사용 객체를 찾으면 JVM은 그 객체들이 점유하고 있는 메모리를 해제하고 추가 객체를 할당하는 데 사용할 수 있습니다.

하지만 대게 이렇게 단순하게 메모리를 해제하고 사용하는 걸로는 충분하지 않습니다. 왜냐하면 어느 순간 메모리를 할당하고 해제하는 걸 반복하다 보면 메모리 단편화(memory fragmentation)이 일어날 수 있기 때문입니다.

이러한 이유로 가비지 컬렉터의 성능은 미사용 객체 찾기, 가용 메모리 생성, 힙 압축과 같은 기본 동작에 따라 좌우 됩니다.
가비지 컬렉터들은 이 동작에 대해 각기 다른 접근 방식을 취하는데 이와같은 이유로 각각의 컬렉터는 성능이 다릅니다.

가비지 컬렉터가 수행되는 동안 실행 중인 애플리케이션을 실행하는 스레드가 없다면 훨씬 동작의 수행은 간단하지만 자바 프로그램은 전형적으로 멀티 스레드 기반으로 가비지 컬렉터 자체가 멀티 스레드를 사용합니다.

논리적은 스래드 그룹을 나눠 표현 하자면 하나는 애플리케이션 로직을 수행하는 스래드 그룹, 나머지는 GC를 수행하는 그룹으로 표현하겠습니다.
GC 스레드가 객체를 참조하고 메모리에 객체를 옮기는 작업을 수행한다면 이는 애플리케이션 스래드가 그 객체를 사용하고 있지 않다는 의미가 됩니다.
즉 가비지컬렉터 스래드는 그 객체에 참조할 수 있는 애플리케이션 스래드가 없을때 작동됩니다.

가비지컬렉터 스래드가 동작하고 모든 애플리케이션 스레드가 중지될 때를 stop the world pause 라고 합니다.
이렇게 중지되면 일반적인 애플리케이션 성능에 가장 큰 영향을 미치므로 가비지 컬랙터를 튜닝한다는건 이 중지가 최소화 되는 걸 의미합니다.

제너레이션 간의 가비지 컬렉터들

세부 사항은 다소 다를 순 있겠지만 모든 가비지 컬렉터는 힙을 별도의 제너레이션으로 나눠서 작업합니다.

이건 올드 제너레이션, 영 제너레이션이라고 불립니다. 때로 에덴 영역은 전체 영 제너레이션을 포함하는 부정확하게 사용되기도 합니다.

영 제너레이션 영역은 에덴(eden)과 서바이버 스페이스(survivor space)로 알려진 구간으로 더 나뉩니다.

이렇게 제너레이션을 나누는 근거는 대부분의 객체는 아주 단기간 동안 사용되다가 폐기된다는 철학에서 나옵니다.
많은 자바 코드에서 객체들은 매우 빠르게 생성되고 폐기됩니다. 이런 형태의 동작을 기반으로 가비지 컬렉터는 많은 객체들이 일시적으로 사용된다는 사실을 이용해 설계되었습니다.

마이너 가비지 컬렉터

객체는 먼저 전체 힙의 일부인 에덴영역에 할당됩니다. 에덴 영역이 가득찼다면 가비지 컬렉터는 애플리케이션 스레드를 모두 멈추고 에덴영역을 서바이서 스페이스 영역 또는 올드 제너레이션 영역으로 옮기는 작업을 합니다. 이 동작을 마이너 가비지라고합니다.

이 설계에는 성능상의 이점이 두가지가 있습니다.

  1. 영 제너레이션은 전체 힙의 일부분일 뿐이므로 이와같이 처리하면 전체 힙을 처리하는 것 보다 더 빠르게 처리할 수 있습니다. 물론 더 자주 멈춘다는 특징이 있습니다. 하지만 더 자주 중지되더라도 대체로 짧게 처리되는 편이 더 이익입니다.

  2. 객체가 영 제너레이션에서 할당되는 방식에서 비롯됩니다. 마이너 가비지 이후에 살아있는 객체는 모두 서바이버 스페이스나 올드 제너레이션으로 이동합니다. 즉 영 제너레이션은 자동으로 압축됩니다.
    모든 가비지 컬렉터 알고리즘은 영 제너레이션에서 수집되는 동안 모든 애플리케이션 스레드를 중지 시킵니다.

객체가 올드 제너레이션으로 이동하고 가득 차게 된다면 JVM은 올드 제너레이션 내에서 더이상 사용되지 않는 객체를 찾아서 폐기 시킬 필요가 있을 것입니다.
여기서 가비지컬렉터 알고리즘은 가장 큰 차이점을 갖게 됩니다.
간단한 방법은 모든 애플리케이션 스레드를 중지시키고 사용되지 않는 객체를 찾아서 메모리를 해제하고 힙을 압축시킵니다. 이 과정을 풀(full) 가비지컬렉터라고 불리고 일반적으로 애플리케이션 스레드가 오래 중지됩니다.

이방법 말고 처리가 복잡한 다른 방법도 있습니다.
CMS와 G1 가비지 컬렉터 모두 애플리케이션 스레드가 실행되는 동안 미상용 객체를 찾을 수 있습니다.
이와같은 이유로 CMS와 G1은 동시 병렬 컬렉터 라고 불립니다. 모든 애플리케이션 스레드를 멈출 필요를 최소화 하기 때문에 저중단 컬렉터라고 불립니다.

물론 이 둘의 동시 병력 컬렉터도 올드 제너레이션을 압축하기 위해 다른 접근법을 취합니다.
CMS나 G1 컬렉터를 이용하면 애플리케이션은 일반적으로 더 적게 중지됩니다. 이에대한 트레이드 오프는 애플리케이션이 전반적으로 CPU를 더 사용한다는 사실입니다.

그렇다고 해서 CMS와 G1 모두 긴 풀 가비지컬렉터를 하지 않는 말은 아닙니다. 풀 가비지 컬렉터 횟수를 줄일 수 있는 상황이 여의치 않다면 풀 가비지컬렉터는 발생할 수 있습니다.

각각의 상황에 맞는 가비지 컬렉터를 선택할 때는 전반적인 성능의 목표에 대해 생각해보면 됩니다. 모든 상황에는 트레이드 오프가 있습니다

애플리케이션에서 개별적인 요청에 대한 응답 시간을 측정한다고 가정해봅시다.

  • 개별적인 요청은 GC에 의한 중지시간 즉 풀 GC에 의해 영향을 받을 수 있습니다. 성능의 목표가 중지 시간이 응답 시간에 미치는 영향을 최소로 하고 싶다면 동시 병렬 컬렉터가 더 적합할 것입니다.
  • 평균 응답 시간이 특이 요소(GC에 의해 중지 시간의 영향으로 응답 시간이 늦춰지는 경우)보다 더 중요하다면 보통 처리율 컬렉터가 더 나은 결과를 낼 수 있습니다.
  • 동시 병렬 컬렉터로 긴 중지 시간을 피하는 이점으로 CPU 사용률에 대한 추가 비용이 들어갑니다.

CPU를 충분히 사용할 수 있는 경우 동시 병렬 컬렉터를 사용한다면 풀 GC를 좀 더 피할 수 있으므로 작업이 더 빨리 끝날 것입니다.

CPU가 제한되어 있다면 동시 병렬 컬렉터는 CPU를 추가적으로 소모하는 오버헤드가 있기 때문에 더 많은 시간이 걸릴 것입니다.

profile
애기 개발자

0개의 댓글