GC 클래스
namespace System
{
public static class GC
{
public static int MaxGeneration { get; }
public static int CollectionCount(int generation);
public static int GetGeneration(object obj);
public static int GetGeneration(WeakReference wo);
public static long GetTotalMemory(bool forceFullCollection);
public static void AddMemoryPressure(long bytesAllocated);
public static void RemoveMemoryPressure(long bytesAllocated);
public static void Collect();
public static void Collect(int generation);
public static void Collect(int generation, GCCollectionMode mode);
public static void Collect(int generation, GCCollectionMode mode, bool blocking);
public static void Collect(int generation, GCCollectionMode mode, bool blocking, bool compacting);
public static void ReRegisterForFinalize(object obj);
public static void SuppressFinalize(object obj);
public static void WaitForPendingFinalizers();
public static bool TryStartNoGCRegion(long totalSize);
public static bool TryStartNoGCRegion(long totalSize, long lohSize);
public static bool TryStartNoGCRegion(long totalSize, bool disallowFullBlockingGC);
public static bool TryStartNoGCRegion(long totalSize, long lohSize, bool disallowFullBlockingGC);
public static void EndNoGCRegion();
public static void KeepAlive(object obj);
public static void RegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold);
public static void CancelFullGCNotification();
public static GCNotificationStatus WaitForFullGCApproach();
public static GCNotificationStatus WaitForFullGCApproach(int millisecondsTimeout);
public static GCNotificationStatus WaitForFullGCComplete();
public static GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout);
}
}
GCSettings 클래스
namespace System.Runtime
{
public static class GCSettings
{
public static GCLatencyMode LatencyMode { get; set; }
public static GCLargeObjectHeapCompactionMode LargeObjectHeapCompactionMode { get; set; }
public static bool IsServerGC { get; }
}
}
자동 쓰래기 수거
- CLR은 자동적인 쓰래기 수거(GC)를 통해서 해제 작업을 진행
- 참조되지 않는, 버림받은 객체가 쓰레기 수거의 대상이 됨
- CLR은 가용 메모리 양과 할당된 메모리 양, 지난 번 수거 이후 지난 시간 등
여러가지 요인을 고려해서 수거 시점을 결정
- 메모리 관리자는 각 객체들을 세대별로 분류하며, 젋은 세대를 늙은 세대보다 더 자주 수거함
- 쓰레기 수거에 쓰이는 시간과 응용 프로그램의 메모리 소비량 사이의 균형을 맞추려고 노력
* 응용 프로그램이 실제로 필요한 것 보다 더 많은 메모리를 소비할 수 있음
뿌리 참조
- 뿌리 객체가 직-간접적으로 참조하지 않은 객체는 쓰레기 수거 대상이 됨
- 뿌리 객체로 간주되는 요소
1. 실행 중인 메서드(혹은 그 메서드의 호출 스택에 있는 모든 메서드)의 지역 변수나 매개 변수
2. 정적 변수
3. 종료 준비가 된 객체들이 담긴 대기열에 있는 객체
- 뿌리 객체로부터 참조 관계를 따라 접근 할 수 없는 객체는 도달 불가능(Unreachable) 객체이며,
쓰레기 수거의 대상이 됨
쓰레기 수거기의 작동 방식
- 표준 CLR의 쓰레기 수거기는 세대별(Generational) 표시 후 압축(Mark and Compact) 쓰레기 수거 알고리즘 사용
- 관리되는 힙에 할당된 객체들의 메모리를 자동으로 관리함
- 추적식(Tracing) 쓰레기 수거기라고도 부름
* 객체에 대한 모든 접근을 일일히 검사하지 않음
* 가끔 깨어나서 관리되는 힙에 저장된 객체들의 그래프를 추적, 수거 대상 객체들을 수거하기 때문
- 수거 시점
1. 메모리가 일정량 이상 할당 된 후 추가로 메모리가 할당(new) 될 때
2. 응용 프로그램의 메모리 사용량을 줄여야 할 필요가 있는 상황
3. System.GC.Collect를 사용해서 쓰래기 수거를 명시적으로 강제
- 수거되지 않고 남은 활성 객체들은 힙의 시작 위치쪽으로 이동(압축)
* 메모리 단편화(fragmentation) 방지
* CLR이 새 객체를 할당 할 때 간단한 전략을 사용 할 수 있음(항상 힙에 끝에서 메모리 할당)
- 쓰레기 수거 후에도 메모리가 부족하고,
운영체제가 새로운 메모리를 제공 할 수 없는 상황이라면 OutOfMemoryException
세대별 수거
- 관리되는 힙을 3가지 세대로 나눔 (Gen0, Gen1, Gen2)
- 할당될 당시에는 Gen0, 쓰레기 수거에서 살아남을 때마다 Gen1, Gen2로 옮겨감
- CRL은 Gen0 구역을 비교적 작게 유지 (일반적으로 수백 KB에서 몇 MB정도)
- Gen0 구역이 다 차면 쓰레기 수거기는 Gen0 수거를 실행, 비교적 자주 발생
- Gen1에 대해서도 비슷한 공간 상한 적용(Gen1은 Gen2의 버퍼 역할 수행)
- Gen2는 시간이 훨씬 오래 걸리므로 덜 자주 수행함
- 단명(Ephemeral)세대인 Gen0, Gen1의 GC는 1ms 미만의 시간이 걸림
- Gen2의 GC는 시간이 더 오래 걸린다. 크기가 무제한이기 때문
LOH (Large Object Heap)
- 85,000 바이트 이상의 객체들을 개별적인 힙(LOH)에 저장함
- 과도한 Gen0 수거를 방지하기 위함
- LOH는 기본적으로 압축되지 않음. 커다란 메모리 불록을 이동하는 것은 비용이 너무 큼
* 객체를 힙에 끝에 할당하는 전략을 사용 할 수 없어 메모리 할당이 더 느릴 수 있음
* 단편화가 일어남. 메모리 구멍을 채우기 어려울 수 있음
- LOH의 모든 객체는 Gen2로 간주됨
- LOH를 강제 압축
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
동시적 배경 수거
- Gen0, Gen1의 경우 수거 과정 전체에서 프로그램 실행 스레드들이 차단됨
- Gen2
* 워크스테이션 버전: Gen2 수거 도중 실행 스레드 동작 허용
* 서버 버전: Gen2 수거 도중 실행 스레드 동작 불허, 가용 모든 코어 사용, 속도 빠름
- CLR 4.0 이전에는 동시 수거(Concurrent Collection): Gen2 수거중 Gen0이 꽉 차면 수거가 동시적이지 않게 됨
- CLR 4.0 이후부터 배경 수거(Background Collection): 한계 없음
GC 알림 (서버 CLR)
- 서버 버전의 CLR은 전체 GC를 수행하기 직전에 그 사실을 프로그램에 통지 할 수 있음
- 서버 팜(Server Farm) 구성용, GC가 실행되기 전에 요청을 다른 서버에게 보내고 GC에 돌입
1. GC.RegisterForFullGCNotification(maxGenerationThreshold, largeObjectHeapThreshold) (인자 설정에 대한 정보)
2. 개별 스래드 -> GC.WaitForFullGCApproach()
3. GCNotificationStatus를 반환 받았다면 곧 수거가 시작된다는 것을 의미
4. 요청들이 다른 서버에게 가도록 설정, 코드에서 CG 강제 수행
5. GC.WaitForFullGCComplete() 수행
6. GCNotificationStatus를 반환 받았다면 수거가 종료
7. 1번부터 반복
쓰래기 수거 강제 실행
- GC.Collect
- 인자로 특정 세대 지정, GCCollectionMode, 차단 여부, 압축 여부 설정 가능
- 수거 시점을 GC가 직접 판단하게 하는 것이 최상의 성능
- 유저가 GC를 직접 강제하면...
1. 불필요한 세대 승격이 일어나 성능이 나빠질 수 있음
2. 성능 최적화를 위해 상한을 자동 조절하는 자체 조율(Self-Tuning)에 방해 될 수 있음
- GC를 직접 강제해야 할 경우
* 활동을 마치고 그 다음 할당까지 오랜 시간이 걸리는 경우
- 종료자들 때문에 수거가 지연된 객체들을 확실히 수거하기
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
쓰래기 수거기의 조율
- GCSettings.LatencyMode
* 잠복지연(Latency)와 전반적인 효율성 사이의 균형을 잡는 방식에 영향을 미침
* Interactive(기본값), LowLatency(더 빨리 실행 될 수 있는 수거 선호, 수거가 더 자주 일어남)
- GC.TryStartNoGCRegion / GC.EndNoGCRegion
* GC의 활동 일시 정지 / 활동 재개
메모리 압력
- GC.AddMemoryPressure
- CLR은 관리되는 메모리만 추적, 프로그램이 비관리 메모리도 사용한다면
응용 프로그램이 실제보다 더 작은 메모리를 사용한다고 오해할 여지가 있음
- 프로그램이 할당한 비관리 메모리의 양을 CLR에게 알려줌
- GC.RemoveMemoryPressure
- AddMemoryPressure로 설정한 메모리 압력을 해제함
메모리 소비량 진단
- GC.GetTotalMemory
- 인수로 true를 주면 먼저 수거를 실행 함
- 메모리 소비량을 알 수 있음
'C#' 카테고리의 다른 글
[C#] Debugger (0) | 2023.10.22 |
---|---|
[C#] Contract, Code Contracts(코드 계약) (0) | 2023.10.20 |
[C#] Debug, Trace (0) | 2023.10.18 |
[C#] WeakReference (0) | 2023.10.14 |
[C#] IDisposable (Dispose), 종료자(Finalizer) (0) | 2023.10.10 |
[C#] IStructuralEquatable, IStructuralComparable (0) | 2023.10.08 |
[C#] StringComparer (0) | 2023.10.06 |
[C#] IComparer<T>, IComparer, Comparer<T> (0) | 2023.10.04 |