본문 바로가기

Computer Science/Java

Java 메모리 - (3) Garbage Collector

Garbage Collector(GC)

 

  • 메모리 관리 방법 중 하나로 시스템에서 더이상 사용하지 않는 동적 할당된 메모리 블럭을 찾아 자동으로 다시 사용 가능한 자원으로 회수하는 것으로 시스템에서 가비지 컬렉션을 수행하는 부분을 가비지 컬렉터라 부릅니다.
  • C언어의 경우, 프로그래머가 메모리를 할당한 뒤 수동으로 해제까지 직접 해줘야 합니다. 그래서 메모리를 할당해놓고 필요없어진 뒤에도 해제를 안하는 일 등이 생겨서 메모리 누수가 생기거나 거꾸로 해제했던 메모리를 다시 사용하는 실수가 생기곤 합니다.
  • 이러한 문제들을 해결하기 위해 생겨난 것이 가비지 컬렉션이며, 가비지 컬렉션 기능을 채택한 언어의 경우에는 가비지 컬렉터가 자동으로 프로그램이 실행되며 생기는 쓸모 없는 메모리들을 알아서 수집하고 관리해줍니다.
  • 즉, 프로그래머는 Heap을 사용할 수 있는 만큼 자유롭게 사용하고, 더이상 사용되지 않는 객체들은 가비지 컬렉션을 담당하는 프로세스가 자동으로 메모리에서 제거하도록 하는 것이 가비지 컬렉션의 기본 개념입니다.
  • JVM의 메모리는 크게 Class 영역, Stack, Heap, Native Method Stack의 4개 영역으로 나뉩니다. 이중 가비지 컬렉터가 인식하고 할당하는 메모리 영역은 Heap 영역입니다.

 

 

Garbage 란?

 

Garbage란 '정리되지 않은 메모리', '유효하지 않은 메모리 주소'를 말합니다.

 

아래 예시를 통해 자세히 알아보겠습니다.


String str = "Garbage";
str += " Collector";

위 코드에서 String str = "Garbage" 구문이 실행된 후 Stack과 Heap의 상태는 아래와 같습니다.


그리고 다음 구문인 str += " Collector"; 구문에서 문자열 더하기 연산이 수행될 때, 기존에 있던 "Garbage" String 값에 " Collector"를 덧붙이는 것이 아니라, 문자열에 대한 더하기 연산이 수행된 결과가 새롭게 Heap 영역에 할당됩니다. 이를 표현하면 아래와 같습니다.


위처럼 Stack에는 새로운 변수가 할당되지 않습니다. 그저 Heap 영역에 새롭게 "Garbage Collector" 가 생성되고, 기존에 "Garbage"를 참조하던 Stack 영역의 str 변수는 새롭게 생성된 "Garbage Collector"를 참조하게 됩니다.

 

이 과정에서 Heap 영역의 "Garbage"를 참조하고 있는 Stack의 변수는 아무것도 없으므로 Unreachable 객체가 되고, 이를 Garbage라고 합니다.

 

즉, 가비지 컬렉터는 메모리가 부족할 때, 이러한 Garbage를 정리해주는 프로그램을 말합니다.

프로그램을 실행하다보면 Garbage가 계속해서 발생하게 되는데, 이들은 유효한 메모리가 아닙니다.

이렇게 생겨난 Garbage들은 사용되지도 않으면서 메모리를 차지하고 있게 됩니다.

따라서, JVM의 가비지 컬렉터는 Garbage를 다른 용도로 사용할 수 있게 '메모리 해제'시키는 프로그램입니다.

 

 

Garbage Collector의 원리

 

GC 작업을 하는 가비지 컬렉터는 다음과 같은 일을 합니다.

  1. 메모리 할당
  2. 사용 중인 메모리 인식
  3. 사용하지 않는 메모리 인식

즉, 메모리가 부족할 때 가비지를 정리해주는 프로그램을 가비지 컬렉터라고 부릅니다. 가비지 컬렉터에 대해 알기 전에 우선 메모리에 대한 이해가 필요한데 프로그램을 실행할 때 메모리를 관리하는 OS에 프로그램 실행에 필요한 메모리를 요청하게 됩니다. 이때 메모리를 어디에 저장할지 그 주소를 할당하는데 이 주소를 offset 주소(상대주소)라고 부릅니다.

이 할당된 메모리들은 프로그램이 돌아가면 필연적으로 가비지가 발생하게 됩니다. 기존에 가리키고 있던 메모리가 새롭게 선언되거나 형변환이 되면서 다른 곳을 가리키게 되고 주소를 잃어버리게 되어 다시 찾을 수 없게 되면서 정리되지 않은 메모리가 생겨버리게 되기 때문입니다.

그래서 가비지 컬렉터는 가비지를 다른 용도로 사용할 수 있도록 메모리를 해제시킵니다. 이것이 가비지 컬렉터의 목적입니다.

Java 기준으로 JVM은 메모리를 부여받고 프로그램을 실행하다가 메모리가 부족해지는 순간이 오면 추가적으로 메모리를 더 요청합니다. 요청하는 바로 이때 가비지 컬렉터가 실행됩니다.

또한, 서버 프로그램의 경우 24시간 내내 돌아가는데, 이 때에는 JVM이 한가할 때(Idle time) 가비지 컬렉터가 실행됩니다.

 

 

Stop-the-world

 

Stop-the-world는 GC 실행을 위해 JVM이 애플리케이션 실행을 멈추는 것입니다. 이 Stop-the-world가 발생하면 GC를 실행하는 스레드를 제외한 나머지 스레드는 모두 작업을 멈춥니다. 그래서 대부분의 경우에서 말하는 GC 튜닝이란 이 Stop-the-world의 시간을 줄이는 것을 의미합니다.

 

GC의 과정을 Mark and Sweep 이라고 부릅니다. 가비지 컬렉터가 닿을 수 있는 모든 변수나 객체들을 스캔하면서 어떤 객체를 가리키고 있는지 찾는 과정을 Mark라 하고, 이 과정에서 Stop-the-world가 발생합니다. 이후 Mark 되어있지 않은 객체들을 Heap 영역에서 제거하는 과정이 Sweep 입니다.

 

 

Gabage Collector의 동작

 

GC의 공간

  • Young 영역
    • 새롭게 생성된 객체는 Young 영역에 생성됩니다.
    • 대부분의 객체가 금방 접근 불가능한 상태가 되기 때문에 많은 객체가 이 영역에 생성되었다가 사라집니다.
    • Minor GC가 발생하는 영역
  • Old 영역
    • Young 영역에서 참조할 수 있는 상태를 유지해 살아남은 객체가 여기로 복사됩니다.
    • 대부분 Young 영역보다 크게 할당하며 크기가 큰 만큼 Young 영역보다는 GC가 적게 발생합니다.
    • Major GC가 발생하는 영역
  • Permanent 영역 (Method Area)
    • 클래스와 메소드 정보와 같이 Java 언어 레벨에서는 거의 사용되지 않는 영역입니다.
    • 객체의 Intern(억류)가 된 문자열 정보를 저장하는 곳
    • GC가 발생하기도 하는데 Major GC의 횟수에 포함됨

 

Young 영역에서의 GC

  • Young 영역은 Eden 영역, Survivor 영역 2개, 총 3개의 영역으로 나눠짐
  • Minor GC가 일어나는 과정
    1. 새로운 객체가 생성되면 Eden 영역에 생성되며, GC 후 살아남은 객체는 Survivor 영역 중 하나로 이동합니다.
    2. Survivor 영역이 가득차게 되면 다른 Survivor로 이동하고 나머지 하나의 Survivor 영역은 빈 상태로 변경합니다.
    3. 이 과정을 반복한 후, 살아남은 객체는 Old 영역으로 이동하게 됩니다.

 

Old 영역에서의 GC

  • Old 영역은 기본적으로 메모리가 가득 차면 GC를 수행합니다.
  • Old 영역에서 일어나는 GC는 아래의 5가지 방식으로 수행됩니다.
    1. Serial GC
      • Mark-Sweep-Compact 알고리즘을 이용합니다.
      • Old 영역에 살아있는 객체를 Mark 하고 Heap의 앞 부분부터 살아있는 객체를 Sweep 한 뒤, Heap의 앞 부분부터 객체를 쌓습니다(Compact).
      • 메모리와 CPU 코어 수가 적을 때 적합한 방식입니다.
    2. Parallel GC
      • Serial GC와 기본적인 알고리즘은 같지만, GC를 처리하는 스레드의 개수가 여러 개입니다.
      • 메모리와 CPU 코어 수가 많을 때 적합할 방식입니다.
    3. Parallel Old GC
      • Parallel GC와 같지만 Old 영역의 GC 알고리즘만 다릅니다.
      • Mark-Summary-Compact 알고리즘을 이용하며, Mark-Sweep-Compact 알고리즘에 비해 조금 더 복잡합니다. (Summary에서 GC를 수행한 영역에 대해서 별도로 살아있는 객체를 식별합니다.)
    4. CMS GC
      • Class Loader에서 가장 가까운 객체 중 살아있는 것만 찾습니다. 후에 참조가 끊기거나 추가된 객체를 확인하고 마지막에는 가비지를 정리하는 작업을 합니다.
      • Stop-the-world의 시간이 짧고 모든 애플리케이션 응답 속도가 매우 중요할 때 사용합니다.
      • CMS GC는 다른 방식보다 메모리와 CPU를 많이 사용합니다.
      • Compact 단계가 기본적으로 제공되지 않습니다.
    5. G1 GC
      • 바둑판의 각 영역에 객체를 할당하고 GC를 실행합니다.
      • 해당 영역이 가득 차면 다른 영역에서 객체를 할당하고 GC를 실행하는 방식입니다.
      • Young 영역과 Old 영역의 구분이 없이 매우 빠르게 객체를 할당하고 GC합니다.

 

 

 

Gabage Collector의 한계

  • 어떤 방식의 가비지 수집을 사용하든지 실행 시간에 작업을 하는 이상 성능 저하를 피할 수는 없습니다.
  • 가비지 수집기가 존재하더라도 더이상 접근이 불가능한 객체만 회수하기 때문에 메모리 누수는 발생할 수 있습니다.

'Computer Science > Java' 카테고리의 다른 글

JAVA vs C++  (0) 2021.03.27
추상 클래스와 인터페이스  (0) 2020.10.23
Java 메모리 - (2) JVM  (0) 2020.10.19
Java 메모리 - (1) Java 메모리 구조  (0) 2020.10.18
객체지향 프로그래밍(OOP)  (0) 2020.10.17