본문 바로가기

ComputerScience/Java

[Java] Garbage Collection

Garbage Collection

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

 

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

  
가비지 컬렉터를 실행하는 JVM의 동작은 일반적으로 프로그램이 종료되기 전까지 혹은 그동안 일어날 수 있으며, 종종 시간이 지난 후에 발생할 수도 있습니다.

그래서 C나 C++ 같은 다른 언어에서 메모리 누수(메모리가 자동으로 회수되지 않기 때문에 발생)는 Java에서는 덜 발생할 가능성이 있습니다.

 

여담으로 메모리 누수 외의 자원 누수도 발생할 수 있습니다.

어플리케이션은 디스크에 있는 파일을 열어 내용을 수정할 수 있습니다. 그러나 애플리케이션이 파일을 닫지 않는다면, 다른 애플리케이션이 파일을 사용하기 전에 해당 애플리케이션이 종료되어야 합니다.

 

일반적인 애플리케이션에서는 가비지 컬렉터(garbage collector)가 결국 수집 대상이 되는 객체들에 대한 메모리를 회수할 수 있습니다.
JVM(Java Virtual Machine)은 가비지 컬렉터가 언제 실행될지, 혹은 실행될지에 대한 보장을 제공하지 않습니다.
가비지 컬렉터가 실행될 때, 모든 객체가 수집되거나 수집 대상 객체 중 일부만 수집될 수도 있습니다.

Memory Heap Generations

자바 가비지 컬렉션 작동 방식을 완전히 이해하기 위해서는 메모리 힙의 다양한 세대에 대해 알아야 합니다. 이러한 세대는 가비지 컬렉션을 더 효율적으로 만드는 데 도움이 되며 다음과 같은 종류의 공간으로 분할됩니다.
1. Eden:
   - 자바의 Eden 공간은 객체가 생성되는 메모리 풀입니다.
   - Eden 공간이 가득 찰 때, 가비지 컬렉터는 사용되지 않는 객체를 제거하거나 아직 사용 중인 경우 Survivor 공간에 저장합니다. 이 공간은 메모리 힙의 young 세대의 일부로 간주됩니다.

2. Survivor:
   - JVM에는 두 개의 Survivor 공간이 있습니다: Survivor 0, Survivor 1.
   - 이 공간 역시 young 세대의 일부입니다.

3. Tenured:
   - Tenured 공간은 오래된 객체가 저장되는 곳입니다.
   - 객체가 일정한 가비지 컬렉션 주기를 생존하면 이 공간으로 이동됩니다.
   - 이 공간은 Eden 공간보다 훨씬 크며, 가비지 컬렉터가 덜 자주 확인합니다.
   - 이 공간은 메모리 힙의 old 세대로 간주됩니다.

이러한 다양한 공간들은 어떻게 가비지 컬렉션을 더 효율적으로 만드는지 설명합니다. 가비지 컬렉션은 많은 새로운 객체가 메모리에 오랫동안 남아 있지 않아도 되기 때문에 가장 빈번하게 Eden 공간에서 발생합니다. 그러나 가비지 컬렉터가 반복해서 수집되지 않은 객체를 계속 확인하는 것은 비효율적입니다. 특히 객체가 오랜 시간 동안 힙에 남아야 할 경우에는 더욱 그렇습니다. 이것은 컬렉터의 비효율적인 사용입니다. 객체를 Survivor 및 Tenured 공간으로 이동함으로써 가비지 컬렉터는 메모리에 계속 필요한 객체가 더 높은 가능성이 있다는 것을 알기 때문에 이러한 영역을 덜 자주 확인합니다. Tenured 공간은 Eden 공간보다 훨씬 크기 때문에 덜 자주 채워지며, 가비지 컬렉터가 덜 확인합니다. 단점은 Tenured 공간이 자주 확인되지 않기 때문에 메모리 누수에 민감할 수 있다는 것입니다.

young 세대(Eden 및 Survivor 공간)의 가비지 컬렉션 주기를 "마이너 가비지 컬렉션"이라고 합니다. old 세대(Tenured 공간)의 가비지 컬렉션 주기는 "오래된 가비지 컬렉션" 또는 "메이저 가비지 컬렉션"으로 알려져 있습니다. 이것은 마이너 가비지 컬렉션보다 오래 걸리기 때문입니다. 마이너 가비지 컬렉션 주기는 더 간단하고 빠른 프로세스이며, 이는 훨씬 빈번하게 발생하며 효율적이어야 하기 때문입니다.

과거 버전의 자바(Java 8 이전)에서는 JVM에 필요한 애플리케이션 메타데이터를 포함하는 permanent generation (perm gen or PermGen) 라고 알려진 메모리 영역이 있었습니다. 그러나 Java 8에서는 permanent generation이 제거되었습니다.

Collecter Options

자바에서는 네 가지 다른 가비지 컬렉터 옵션이 제공됩니다. 각각 장단점이 있습니다.

1. Serial 가비지 컬렉터
   - 주로 작고 단일 스레드 환경에서 사용됩니다.
   - 프로덕션 환경에서 사용하지 않는 것이 좋습니다. 왜냐하면 가비지 컬렉션 프로세스가 스레드를 점유하며 다른 프로세스를 멈추게 만들기 때문입니다. 이를 "stop-the-world" 이벤트라고 합니다.

   - Serial GC는 데스크톱의 CPU 코어가 하나만 있을 때 사용하기 위해서 만든 방식입니다. Serial GC를 사용하면 애플리케이션의 성능이 많이 떨어집니다.

2. Parallel 가비지 컬렉터
   - JVM의 기본 가비지 컬렉터입니다.
   - 여러 (병렬) 스레드를 사용합니다.
   - 다중 CPU도 사용하여 처리량을 높일 수 있으므로 처리량 컬렉터로도 알려져 있습니다. 그러나 가비지 컬렉션을 실행할 때 응용 프로그램 스레드도 멈출 수 있습니다.

3. Concurrent Mark-and-Sweep (CMS) 컬렉터
   - 병렬 가비지 컬렉터와 비슷하게 여러 스레드를 사용합니다.
   - "Low Latency" 컬렉터로 알려져 있으며 응용 프로그램 스레드를 덜 자주 멈추게 합니다. 이것은 "stop-the-world" 이벤트가 사용자를 위해 문제를 일으킬 수 있는 사용자 지향 애플리케이션에 더 적합합니다. 그러나 젊은 세대를 수집할 때에도 실행을 멈추어야 합니다. 또한, 컬렉터 스레드가 응용 프로그램 스레드와 동시에 실행되기 때문에 다른 가비지 컬렉터보다 더 많은 처리량을 사용합니다.

4. Garbage First (G1) 가비지 컬렉터
   - G1 가비지 컬렉터는 다른 방식으로 작동합니다. 다른 가비지 컬렉터가 young과 old generations를 별도로 수집하는 반면, G1은 힙을 여러 영역으로 분할하여 동시에 모두 수집할 수 있습니다. 이로써 큰 영역을 한꺼번에 비우는 대신 작은 영역을 비울 수 있어 컬렉션 프로세스를 최적화할 수 있습니다. 이는 CMS 컬렉터처럼 동시에 실행되며 거의 실행을 멈추지 않고 young과 old generations를 모두 수집할 수 있습니다.

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

 

참고 자료

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