GC가 작동하는 시기
- 객체를 할당하여 할당하는 임계치가 넘어갈 때 (각 세대 별)
- 시스템의 메모리가 부족할 때
- GC.Collect 메서드를 호출할 때
GC Root
루트는 힙에 있는 최상위 객체를 가리키는 참조를 말한다 스택이나 힙(static)에 생성된다.
.NET 어플레이케이션을 실행하면, JIT 컴파일러가 루트 목록을 생성하고 CLR이 루트 목록을 돌면서 상태를 갱시하는 것이다. (GC가 참조함)
가비지 컬렉터는 루트 목록을 순회하면서 루트가 참조하는 힙 객체와 관계를 조사한다.
- 어떤 힙과도 루트와 관계가 없다면 필요 없는 Garbage
- 다른 힙 객체를 참조한다면 Not Garbage
쓰레기 객체가 있던 메모리는 비워줘야 한다.(Sweap)
이 때문에 이러한 방법의 특징은 Heap 전체를 검사할 필요가 없어 C/C++보다 빠르다. 메모리 할당 시 메모리 조각 현상이 발생하지 않는다. 저 부분을 비워주고 땡겨준다 (Compaction) 루트 목록에 대한 조사 이후 Garbage Collector가 힙을 순회하며 비어있는 공간에 인접 객체들을 이동시켜준다.
관리되는 힙
- CLR에 의해 가비지 컬렉터가 초기화되고 나면 가비지 컬렉터가 개체를 저장 및 관리하기 위해 메모리 세그먼트를 할당한다. 이 메모리를 관리되는 힙이라고 하며, 이는 운영 체제의 네이티브 힙과 대조된다.
- 관리되는 각 프로세스마다 관리되는 힙이 있다. 프로세스의 모든 스레드는 같은 힙에 개체 메모리를 할당한다.
- 메모리를 예약하기 위해 가비지 컬렉터는 Windows VirtualAlloc 함수를 호출하며 관리되는 애플리케이션을 위해 한 번에 하나의 메모리 세그먼트를 예약한다.
- 필요할 때 세그먼트를 예약하고 Windows VirtualFree 함수를 호출하여 (세그먼트에서 개체를 지운 후) 세그먼트를 운영 체제로 돌려보낸다.
CLR은 메모리를 효율적으로 관리하기 위해 세대를 나눈다 0,1,2이다.
- 0세대는 GC가 아직 적용하지 않은 객체
- 1세대는 0세대와 2세대 사이
- 2세대는 GC를 2번 이상하고도 힙에 저장되어 있는 객체
가비지 컬렉션이 실행되면 0 > 1 > 2 순으로 세대가 바뀌거나 사라지게 할 것이다.
2세대 힙이 오브젝트로 가득차게 된다면 (Full GC) CLR이 해당 어플리케이션을 일시 중단한 뒤 모든 세대에 대해 다시 GC를 실행한다. (어플레이케이션이 차지하는 메모리가 크면 중지시간 길어짐, 멀티스레드로 영향을 덜 끼치게끔 할 수가 있다.)
임시 세대 및 세그먼트
- 0세대와 1세대의 개체는 수명이 짧으므로 이러한 세대를 임시 세대라고 한다.
- 임시 세대는 임시 세그먼트라는 메모리 세그먼트에 할당된다. 가비지 수집기에서 획득하는 새로운 각 세그먼트는 새로운 임시 세그먼트가 되며 0세대 가비지 수집에서 남은 개체를 포함한다. 이전의 임시 세그먼트는 새로운 2세대 세그먼트가 된다.
- 임시 세그먼트의 크기는 시스템이 32비트 또는 64비트인지, 그리고 실행 중인 가비지 수집기 형식(워크스테이션 또는 서버 GC)에 따라 달라진다. 다음 표에서는 임시 세그먼트의 기본 크기를 보여 준다.
워크스테이션 GC | 16MB | 256 MB |
서버 GC | 64MB | 4 GB |
4개 논리 CPU가 있는 > 서버 GC | 32MB | 2GB |
논리 CPU가 8개인 > 서버 GC | 16MB | 1GB |
임시 세그먼트에는 2세대 개체가 포함될 수 있다. 2세대 개체는 여러 세그먼트를 사용할 수 있다(프로세스에 필요하고 메모리가 허용하는 한도만큼).
가비지 컬렉션 중 수행되는 작업
- 모든 활성 개체를 찾아 목록을 만드는 표시 단계
- 압축될 개체에 대한 참조를 업데이트하는 재배치 단계
- 비활성 개체에 의해 점유된 공간을 회수하고 남은 개체를 압축하는 압축 단계. 압축 단계에서는 가비지 수집에서 남은 개체가 세그먼트의 오래된 쪽으로 이동된다.
- 2세대 수집은 여러 세그먼트를 점유할 수 있으므로 2세대로 승격된 개체는 오래된 세그먼트로 이동될 수 있다. 1세대 및 2세대 남은 개체는 2세대로 승격되므로 모두 다른 세그먼트로 이동될 수 있다.
- 일반적으로 대형 개체를 복사하면 성능 저하가 발생하기 때문에 LOH(대형 개체 힙)는 압축되지 않는다. 하지만 .NET Core 및 .NET Framework 4.5.1 이상에서, GCSettings.LargeObjectHeapCompactionMode 속성을 사용하면 필요 시 대형 개체 힙을 압축시킬 수 있다. 또한 다음 중 하나를 지정하여 하드 한도가 설정된 경우 LOH가 자동으로 압축된다. (컨테이너의 메모리 제한) , (GCHeapHardLimit 또는 GCHeapHardLimitPercent 런타임 구성 옵션)
가비지 수집기는 다음 정보를 사용하여 개체가 활성 개체인지 여부를 판단한다.
- 스택 루트. JIT(Just-In-Time) 컴파일러 및 스택 워크에서 제공한 스택 변수 JIT 최적화는 가비지 컬렉터로 보고되는 스택 변수 내에서 코드 영역을 늘리거나 줄일 수 있다.
- 가비지 수집 핸들. 관리되는 개체를 가리키며 사용자 코드 또는 공용 언어 런타임에 의해 할당될 수 있는 핸들이다.
- 정적 데이터. 다른 개체를 참조할 수 있는 애플리케이션 도메인의 정적 개체이다. 각 애플리케이션 도메인은 해당 정적 개체를 추적한다.
가비지 수집이 시작되기 전에 가비지 수집을 트리거한 스레드를 제외한 모든 관리되는 스레드가 일시 중단된다.
다음 그림에서는 가비지 수집을 트리거하여 다른 스레드가 일시 중단되도록 하는 스레드를 보여 준다.
관리되지 않는 리소스
- 가비지 수집기는 사용자 애플리케이션에서 만들어지는 대부분의 개체에 대해 메모리 관리 작업을 자동으로 수행할 수 있다. 하지만 관리되지 않는 리소스의 경우는 명시적으로 정리할 필요가 있다. 가장 일반적인 형태의 관리되지 않는 리소스로는 파일 핸들, 창 핸들 또는 네트워크 연결 등의 운영 체제 리소스를 래핑하는 개체를 들 수 있다. 가비지 수집기에서는 관리되지 않는 리소스를 캡슐화하는 데 사용되는 관리되는 개체의 수명을 추적할 수 있지만, 리소스 정리 방법에 대한 구체적인 정보는 알 수 없다.
- 관리되지 않는 리소스를 캡슐화하는 개체를 정의하는 경우 공용 Dispose 메서드에서 관리되지 않는 리소스를 정리하는 데 필요한 코드를 제공하는 것이 좋다. 메서드를 Dispose 제공하면 개체 사용자가 개체를 완료할 때 리소스를 명시적으로 해제할 수 있다. 관리되지 않는 리소스를 캡슐화하는 개체를 사용하는 경우 필요에 따라 Dispose를 호출해야 한다.
- 형식의 소비자가 실수로 Dispose를 호출하지 않은 경우 관리되지 않는 리소스를 해제하는 방법도 제공해야 한다. 안전한 핸들을 사용하여 관리되지 않는 리소스를 래핑하거나 Object.Finalize() 메서드를 재정의할 수 있다.
관리되지 않는 리소스 정리에 대한 자세한 내용은 관리되지 않는 리소스 정리를 참조.
출처 : https://luv-n-interest.tistory.com/922
출처 : https://docs.microsoft.com/ko-kr/dotnet/standard/garbage-collection/fundamentals
'프로그래밍 언어 > C#' 카테고리의 다른 글
C# 난수 생성 Random 클래스 (0) | 2022.10.24 |
---|---|
C# 구조체가 IEquatable<T>를 상속해야 하는 이유 (0) | 2022.08.21 |
C# ILookup과 Lookup<TKey, TElement>와 Dictionary<TKey, TValue>간 차이 (0) | 2022.08.07 |
C# 람다식 (lambda expression) (0) | 2022.08.06 |
C# 가변 길이 배열 (Variable Length Array) (0) | 2022.08.02 |