[Java] Garbage Collection
본문 바로가기

ComputerScience/Java

[Java] Garbage Collection

가비지 컬렉션(Garbage Collection) 이해하기

모든 객체는 메모리와 같은 시스템 리소스를 사용합니다. 더 이상 필요하지 않은 경우, 이러한 리소스를 시스템에 반환할 수 있는 체계적인 방법이 필요합니다. 그렇지 않으면 "리소스 누수(resource leaks)"가 발생할 수 있습니다.

JVM과 자동 가비지 컬렉션

JVM(Java Virtual Machine)은 더 이상 사용되지 않는 객체가 차지한 메모리를 회수하기 위해 자동 가비지 컬렉션을 수행합니다. Java 객체는 프로그램에 할당된 메모리 섹션인 힙(Heap)에 생성됩니다. 객체가 더 이상 필요하지 않을 때, 가비지 컬렉터는 이러한 사용되지 않는 객체를 찾아 추적하고 메모리를 확보하기 위해 삭제합니다. 가비지 컬렉션 없이는 힙이 결국 메모리 부족으로 인해 OutOfMemoryError 런타임 오류가 발생할 수 있습니다.

 

가비지 컬렉터는 프로그램이 종료되기 전까지 언제든지 실행될 수 있으며, 종종 시간이 지난 후에 발생할 수도 있습니다. 따라서 C나 C++과 같은 언어에서 메모리 누수가 발생할 가능성이 높은 반면, Java에서는 자동으로 메모리를 관리하기 때문에 메모리 누수가 덜 발생할 가능성이 있습니다.

메모리 및 자원 누수

메모리 누수 외에도 자원 누수가 발생할 수 있습니다. 예를 들어, 애플리케이션이 디스크에 있는 파일을 열어 내용을 수정한 후 파일을 닫지 않으면, 다른 애플리케이션이 해당 파일을 사용하기 전에 해당 애플리케이션이 종료되어야 합니다. 일반적인 애플리케이션에서는 가비지 컬렉터가 결국 수집 대상이 되는 객체들에 대한 메모리를 회수할 수 있습니다. 그러나 JVM은 가비지 컬렉터가 언제 실행될지, 혹은 실행될지에 대한 보장을 제공하지 않습니다. 가비지 컬렉터가 실행될 때, 모든 객체가 수집되거나 일부만 수집될 수도 있습니다.

메모리 힙 세대(Memory Heap Generations)

자바 가비지 컬렉션의 작동 방식을 완전히 이해하려면 메모리 힙의 다양한 세대에 대해 알아야 합니다. 이러한 세대는 가비지 컬렉션을 더 효율적으로 만드는 데 도움이 됩니다. 힙은 다음과 같은 공간으로 분할됩니다.

 

1. Eden

  • 설명: 객체가 생성되는 메모리 풀입니다.
  • 특징: Eden 공간이 가득 차면, 가비지 컬렉터는 사용되지 않는 객체를 제거하거나 아직 사용 중인 경우 Survivor 공간으로 이동시킵니다.
  • 세대: Young 세대의 일부입니다.

2. Survivor

  • 설명: JVM에는 두 개의 Survivor 공간이 있습니다: Survivor 0과 Survivor 1.
  • 특징: Eden에서 살아남은 객체들이 일시적으로 저장되는 공간입니다.
  • 세대: Young 세대의 일부입니다.

3. Tenured (Old Generation)

  • 설명: 오래된 객체가 저장되는 공간입니다.
  • 특징: 객체가 일정 주기 동안 가비지 컬렉션을 견디면 이 공간으로 이동됩니다. Eden 공간보다 훨씬 크며, 가비지 컬렉터가 덜 자주 확인합니다.
  • 세대: Old 세대로 간주됩니다.

이러한 다양한 공간은 가비지 컬렉션을 더 효율적으로 만드는 데 기여합니다. 대부분의 가비지 컬렉션은 Eden 공간에서 발생하며, 객체가 Survivor 및 Tenured 공간으로 이동함으로써 가비지 컬렉터는 메모리에 계속 필요한 객체를 덜 자주 확인하게 됩니다. Tenured 공간은 크기가 크기 때문에 덜 자주 채워지며, 가비지 컬렉터가 덜 확인합니다. 단점으로는 Tenured 공간이 덜 자주 확인되기 때문에 메모리 누수에 민감할 수 있습니다.

가비지 컬렉션 주기

  • Young 세대 (Eden 및 Survivor): "마이너 가비지 컬렉션"이라고 하며, 더 자주 발생하고 빠릅니다.
  • Old 세대 (Tenured): "오래된 가비지 컬렉션" 또는 "메이저 가비지 컬렉션"으로 알려져 있으며, 마이너 가비지 컬렉션보다 오래 걸립니다.

참고: Java 8 이전에는 JVM에 perm gen(permanent generation)이라는 메모리 영역이 있었으나, Java 8부터는 제거되었습니다.

가비지 컬렉터 옵션

Java에서는 네 가지 다른 가비지 컬렉터 옵션을 제공합니다. 각각의 가비지 컬렉터는 고유한 장단점을 가지고 있습니다.

 

1. Serial 가비지 컬렉터

  • 특징: 단일 스레드 환경에서 사용됩니다.
  • 장점: 간단한 구현.
  • 단점: 프로덕션 환경에서는 사용하지 않는 것이 좋습니다. 가비지 컬렉션 프로세스가 스레드를 점유하여 다른 프로세스를 멈추게 하기 때문입니다("stop-the-world" 이벤트). CPU 코어가 하나일 때 사용하기 위해 설계되었으나, 애플리케이션 성능에 큰 영향을 미칩니다.

2. Parallel 가비지 컬렉터

  • 특징: JVM의 기본 가비지 컬렉터입니다. 여러 병렬 스레드를 사용하여 처리량을 높입니다.
  • 장점: 다중 CPU를 활용하여 높은 처리량을 제공합니다.
  • 단점: 가비지 컬렉션을 실행할 때 애플리케이션 스레드도 멈출 수 있습니다.

3. Concurrent Mark-and-Sweep (CMS) 컬렉터

  • 특징: 여러 스레드를 사용하여 병렬로 작동합니다.
  • 장점: "Low Latency" 컬렉터로 알려져 있으며, 애플리케이션 스레드를 덜 자주 멈춥니다. 사용자 지향 애플리케이션에 적합합니다.
  • 단점: 젊은 세대를 수집할 때에도 실행을 멈추어야 하며, 컬렉터 스레드가 애플리케이션 스레드와 동시에 실행되기 때문에 더 많은 처리량을 사용합니다.

4. Garbage First (G1) 가비지 컬렉터

  • 특징: 힙을 여러 영역으로 분할하여 동시에 모두 수집합니다.
  • 장점: 큰 영역을 한꺼번에 비우는 대신 작은 영역을 비워 컬렉션 프로세스를 최적화합니다. CMS 컬렉터처럼 거의 실행을 멈추지 않고 young과 old 세대를 모두 수집할 수 있습니다.
  • 단점: 복잡한 구현으로 인해 일부 상황에서는 예상치 못한 동작을 할 수 있습니다.

가비지 컬렉션 강제 실행

가비지 컬렉션을 강제로 실행할 수는 없지만, JVM이 힙을 거의 100% 사용하고 있는 경우에 가비지 컬렉션을 유도할 수는 있습니다. Java 객체가 가비지 컬렉션되도록 보장하기 위해 몇 가지 트릭을 사용할 수 있습니다.

 

참고 자료

https://d2.naver.com/helloworld/1329

https://newrelic.com/blog/best-practices/java-garbage-collection

 

Java garbage collection: What is it and how does it work?

Learn about how Java garbage collection works and how you can monitor your application to ensure garbage collection isn't impacting performance.

newrelic.com

https://www.itworld.co.kr/news/224419

 

'finalize 메소드 퇴역 이후' 자바 오류를 처리하고 클린업하는 방법

몇 년 간의 무성한 소문 끝에 마침내 자바가 JDK 18에서 finalize 메서드를 퇴역시킬 준비를 하고 있다. JDK 향상 제안(Enhanc

www.itworld.co.kr