본문 바로가기
👨‍🏫Study/JAVA

[JAVA] GC(Garbage Collection)2

by 코푸는 개발자 2022. 5. 2.
728x90

Garbage Collection?

.NET의  GC(Garbage Collection)는 애플리케이션의 메모리 할당 및 해제를 관리한다. 새 개체를 만들 때마 공용 언어 런타임이 관리되는 힙에서 개체에 대해 메모리를 할당한다. 관리되는 힙에서 주소 공간을 사용할 수 있다면 런타임은 계속해서 새 개체에 공간을 할당한다. 그러나 메모리는 무한하지 않다. 결국 GC는 메모리를 확보하기 위해 수집을 수행해야 한다. GC의 최적화 엔진은 수행 중인 할당에 따라 수집을 수행하기에 가장 적합한 시간을 결정한다. GC는 수집을 수행할 때 애플리케이션에서 관리되는 힙에 있는 더 이상 사용하고 있지 않은 개체를 확인하고 해당 메모리를 회수하는데 필요한 작업을 수행한다.

 

CLR(공용 언어 런타임)에서 GC는 자동 메모리 관리자 역할을 한다. GC는 애플리케이션의 메모리 할당 및 해제를 관리한다. 즉, 관리 코드를 사용하여 작업하는 개발자는 메모리 관리 작업을 수행하기 위해 코드를 작성할 필요가 없다. 자동 메모리 관리를 사용하면 실수로 개체 비우기를 수행하지 않거나 메모리 누수를 유발하거나 또는 이미 비워진 개체를 찾기위해 메모리에 액세스하려는 경우 등의 일반적인 문제를 해결할 수 있다.

 

이점

GC는 다음과 같은 이점을 제공한다.

  • 개발자가 수동으로 메모리를 해제할 필요가 없다.
  • 관리되는 힙에 효율적으로 개체를 할당한다.
  • 더 이상 사용되지 않는 개체를 회수하고 이러한 개체의 메모리를 비워 이후 할당에서 이 메모리를 사용할 수 있도록 한다.
  • 개체가 다른 개체에 할당된 메모리 자체를 사용할 수 없도록 하여 메모리 보안을 제공한다.

메모리 기본 사항

다음 목록은 중요한 CLR 메로리 개념을 요약한 것이다.

  • 각 프로세스에는 고유한 개별 가상 주소 공간이 있다. 동일 컴퓨터의 모든 프로세스는 동일한 실제 메모리와 페이지 파일(있는 경우)을 공유한다.
  • 기본적으로 32비트 컴퓨터에서는 각 프로세스에 2GB 사용자 모드 가상 주소 공간이 포함된다.
  • 애플리케이션 개발자는 가상 주소 공간만 사용하고 실제 메모리는 직접적으로 조작하지 않는다. GC는 관리되는 힙에서 사용자 대신 가상 메모리를 할당 및 해제한다.                                                                                                                                  네이티브 코드를 작성 중인 경우 Windows  함수를 사용하여 가상 주소 공간을 작업한다. 이러한 함수는 네이티브 힙에서 사용자 대신 가상 메모리를 할당 및 해제한다.
  • 가상 메모리는 다음 세가지 상태일 수 있다.
시스템 상태 설명
 Free 메모리 블록에 가상 메모리에 대한 참조가 없으며, 메모리 블록을 할당에 사용할 수 있다.
예약됨 메모리 블록을 사용자의 작업에 사용할 수 있으며, 다른 할당 요청에는 메모리 블록을 사용할 수 없다. 하지만 커밋되기 전까지는 메모리 블록에 데이터를 저장할 수 없다.
커밋됨 메모리 블록이 실제 스토리지에 할당되어 있다.
  • 가상 주소 공간은 조각화될 수 있다. 즉, 주소 공간에 구멍이라도 부르는 빈 블록이 존재한다. 가상 메모리 할당이 요청된 경우 가상 메모리 관리자는 할당 요청을 만족시킬 수 있도록 충분히 큰 단일 빈 블록을 찾아야 한다. 2GC의 여유 공간이 있는 경우에도 전체 여우 공간이 한 주소 블록에 있는 경우가 아니면 2GB가 필요한 할당이 실패할 수 있다.
  • 예약할 가상 주소 공간이나 커밋할 실제 공간이 충분하지 않은 경우 메모리 부족이 발생할 수 있다.                                       페이지 파일은 실제 메모리 압력(즉, 실제 메모리에 대한 요구)이 낮더라도 사용된다. 실제 메모리 압력이 처음으로 높아지면 운영체제가 데이터를 저장하기 위해 실제 메모리에 공간을 만들어야 하며, 실제 메모리에 있는 데이터 중 일부를 페이지 파일로 백업한다. 필요할 대까지는 데이터가 페이지 파일로 저장되지 않으므로 실제 메모리 압력이 낮은 상황에서도 페이징이 발생할 수 있다.

메모리 할당

사용자가 새 프로세스를 시작하면 런타임에서는 인접한 주소 공간 영역을 이 프로세스에 예약한다. 이 예약된 주소 공간을 관리되는 힙이라고 한다. 관리되는 힙에서는 힙에 있는 다음 개체가 할당될 주소의 포인터를 관리한다. 초기에 이 포인터는 관리되는 힙의 기본 주소로 설정된다. 모든 참조 형식은 관리되는 힙에 할당된다. 애플리케이션에서 참조 형식을 처음으로 만드는 경우, 이 참조 형식에 대한 메모리는 관리되는 힙의 기본 주소로 할당된다. 애플리케이션에서 두 번째 개체를 만드는 경우, GC는 첫 번째 개체와 바로 인접한 주소 공간에 두 번째 개체의 메모리를 할당한다. 주소 공간을 사용할 수 있다면 GC는 이런 방식으로 계속해서 새 개체에 주소 공간을 할당한다. 

 

관리되는 힙에서 메모리를 할당하면 관리되지 않는 힙에서 메모리를 할당하는 것보다 속도가 더 빠르다. 런타임에서는 포인터에 값을 더하여 객체에 메모리를 할당하기 때문에, 스택에서 메모리를 할당하는 속도만큼 빠르다. 또한, 연속으로 할당된 새 개체는 관리되는 힙에 인접하여 저장되므로 애플리케이션에서 개체에 빠른 속도로 액세스할 수 있다.

 

메모리 해제

GC의 최적화 엔진은 수행 중인 할당에 따라 수집을  수행하기에 가장 적합한 시간을 결정한다. 수집을 수행할 때 GC는 애플리케이션에서 더 이상 사용되지 않는 개체에 대한 메모리를 해제한다. GC는 애플리케이션의 루트를 검사하여 더 이상 사용되지 않는 개체를 결정한다. 애플리케이션 루트에는 정적 필드, 스레드 스택의 지역 변수, CPU 레지스터, GC 핸들, finalize 큐가 포함된다. 각 루트는 관리되는 힙에 있는 개체를 참조하거나 Null로 설정된다. GC는 나머지 런타임에 이러한 루트를 요청할 수 있다. GC는 이 목록을 사용하여 그래프를 만드는데, 이 그래프에는 루트에서 연결할 수 있는 모든 개체가 포함되어 있다.

 

그래프에 없는 개체는 애플리케이션 루트에서 연결할 수 없습니다. GC는 연결할 수 없는 개체를 가비지로 간주하고 이 개체에 할당된 메모리를 해제할 것이다. 수집을 수행할 때 GC는 연결할 수 없는 개체에서 사용되는 주소 공간 블록을 찾기 위해 관리되는 힙을 검사한다. 연결할 수 없는 개체가 발견되면 GC는 메모리 복사 기능을 사용하여 메모리에서 연결할 수 있는 개체를 압축한다. 그러면 연결할 수 없는 개체에 할당된 주소 공간 블록이 해제된다. 연결할 수 있는 개체의 메모리가 압축되면 GC는 포인터의 위치를 적절할게 수정한다. 그러면 애플리케이션 루트는 개체의 새 위치를 가리킬 수 있다. 또한 GC는 관리되는 힙의 포인터 위치를 연결할 수 있는 마지막 개체 다음에 지정한다.

 

컬렉션에서 연결할 수 없는 개체의 수가 많이 발견되는 경우에만 메모리를 압축한다. 수집을 수행한 후에도 관리되는 힙에서 모든 개체가 그대로 남아 있다면 메모리 압축을 수행할 필요가 없다.

 

런타임에서는 성능 향상을 위해 큰 개체의 메모리를 별도의 힙에 할당한다. 그러면 GC는 큰 개체에 할당된 메모리를 자동으로 해제한다. 하지만 메모리에서 큰 개체가 이동하는 것을 피하기 위해 일반적으로 이 메모리는 압축되지 않는다.

 

GC 조건

GC는 다음 조건 중 하나가 충족될 경우 발생한다.

  • 시스템의 실제 메모리가 부족하다. 이는 OS의 메모리 부족 알림 또는 호스트에서 표시되는 메모리 부족을 통해 갑지된다.
  • 관리되는 힙의 할당된 개체에 사용되는 메모리가 허용되는 임계값을 초과한다. 이 임계값은 프로세스가 실행됨에 따라 계속 조정된다.
  • GC.Collect 메서드가 호출된다. GC가 지속적으로 실행되므로 이 메서드를 호출해야 하는 경우는 거의 없다. 이 메소드는 주로 특이한 상황 및 테스트에 사용된다.

 

가비지 컬렉션 중 수행되는 작업

GC는 다음 단계로 구성된다.

모든 활성 개체를 찾아 목록을 만드는 표시 단계

압축될 개체에 대한 참조를 업데이트하는 재배치 단계

비활성 개체에 의해 점유된 공간을 회수하고 남은 개체를 압축하는 압축 단계, 압축 단계에서는 GC에서 남은 개체가 세그먼트의 오래된 쪽으로 이동된다.

 

GC는 다음 정보를 사용하여 개체가 활성 개체인지 여부를 판다한다.

  • 스택 루트. JIT(Just-In-Time) 컴파일러 및 스택 워크에서 제공한 스택 변수 JIT 최적화는 GC로 보고되는 스택 변수 내에서 코드 영역을 늘리거나 줄일 수 있다.
  • GC 핸들. 관리되는 개체를 가리키며 사용자 코드 또는 공용 언어 런타임에 의해 할당될 수 있는 핸들이다.
  • 정적 데이터. 다른 개체를 참조할 수 있는 애플리케이션 도메인의 정적 개체이다. 각 애플리케이션 도메인은 해당 정적 개체를 추적한다.

 

GC이 시작되기 전에 GC를 트리거한 스레드를 제외한 모든 관리되는 스레드가 일시 중단된다.

다음 그림에서는 GC를 트리거하여 다른 스레드가 일시 중단되도록 하는 스레드를 보여준다.

 

워크스테이션 및 서버 GC

GC는 자체 조정되며 다양한 시나리오에서 작동 가능하다. 그러나 워크로드의 특서에 따라 GC 형식을 설정할 수 있다. CLR은 다음 유형의 다비지 수집을 제공한다.

  • 워크스테이션 GC는 클라이언트 앱용으로 설계되었다. 독립 실행형 앱의 기본 GC 버전이다. 호스트된 앱의 경우, 호스트는 기본 GC 버전을 결정한다. 워크스테이션 GC는 동시 수집 또는 비동시 수집일 수 있다. 동시(또는 백그라운드) 가비지 수집을 통해 가비지 수집 중 관리되는 스레드가 잡어을 계속 수행할 수 있다. 백그라운드 GC가 .NET Framework 4 이상 버전에서 동시 GC를 대체한다.
  • 높은 처리 속도 및 확장성이 필요한 서버 애플리케이션을 위한 서버 GC
    • .NET Core에서 서버 GC는 비동시 또는 백그라운드 작업일 수 있다.
    • .NET Framework 4.5 이상 버전에서 서버 가비지 수집은 비동시 또는 백그라운드 작업일 수 있다. .NET Framework 4 이전 버전에서는, 서버 가비지 수집이 동시에 수행되지 않는다.

 

다음 그림에서는 서버에서 GC를 수행하는 전용 스레드를 보여준다.

 

 

 

728x90

'👨‍🏫Study > JAVA' 카테고리의 다른 글

[JAVA] JNDI(Java Naming and Directory Interface)  (0) 2022.04.27
[JAVA] Effective Java 1  (0) 2022.04.25
[JAVA] EJB 추가 정리  (1) 2022.04.22
[JAVA] GC(Garbage Collection) 영역별 데이터 흐름 정리  (0) 2022.04.21
[JAVA] G1GC  (0) 2022.04.20

댓글