# README
## 참고 자료
* [메모리 할당 감소](https://docs.microsoft.com/ko-kr/windows/uwp/debug-test-perf/improve-garbage-collection-performance#reduce-memory-allocations)
* [가비지 컬렉션 (GarbageCollection)](https://nomad-programmer.tistory.com/235)
* [EventPipe](https://docs.microsoft.com/ko-kr/dotnet/core/diagnostics/eventpipe)
* [EventPipe 의 Event 종류](https://docs.microsoft.com/ko-kr/dotnet/fundamentals/diagnostics/runtime-garbage-collection-events#gcsuspendeebegin_v1-event)
* [.Net 5 의 GC 성능 향상](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/#gc)
* [8 Techniques to Avoid GC Pressure and Improve Performance in C# .NET](https://michaelscodingspot.com/avoid-gc-pressure/)
* [Spying on .NET Garbage Collector with .NET Core EventPipes](https://medium.com/criteo-engineering/spying-on-net-garbage-collector-with-net-core-eventpipes-9f2a986d5705)
* [FlatSharp Pool 미지원 및 대체 방안](https://github.com/jamescourtney/FlatSharp/issues/263)
* [진단 클라이언트 라이브러리](https://docs.microsoft.com/ko-kr/dotnet/core/diagnostics/diagnostics-client-library)
* [가비지 컬렉션 ETW 이벤트](https://docs.microsoft.com/ko-kr/dotnet/framework/performance/garbage-collection-etw-events)
* [What Is Faster In C#: A Struct Or A Class?](https://mdfarragher.medium.com/whats-faster-in-c-a-struct-or-a-class-99e4761a7b76)
* When you’re storing more than 30–40 bytes of data, use a class.
* When you are storing reference types, use a class.
* When you are storing up to a few thousand instances, use a class.
* When you list is long-lived, use a class.
* In all other cases, use structs.
## GC
* Ref.
* "https://docs.microsoft.com/ko-kr/dotnet/standard/garbage-collection/fundamentals#conditions-for-a-garbage-collection"
* GC 실행 조건
* OS 시스템 메모리 부족
* 관리되는 힙 메모리 임계값 초과
* GC.Collect 호출
* GC gen(세대)
* 각 세대에 메모리가 부족한 경우 해당 세대에 대해 GC 가 발생한다.
* 0 gen(임시 세대) : 짧은 수명 개체
* 1 gen(임시 세대) : 짧은 수명 개체. 0 과 1의 버퍼
* 2 gen : 긴 수명 개체
* 3 gen : LOH(대량 개체 힙). 2 세대에서 GC 되기도한다.
* GC 가 수행하는 작업
* 활성 개체 목록 작성
* 개체의 참조 업데이트
* 비활성 개체 회수 및 생존 개체 압축. LOH 는 압축하지 않음.
* GC 중인 스레드 외에는 모두 정지 시킴.
* 서버 GC(백그라운드 GC) 는 multi thread.
## Test
Struct, Class 생성하여 GC 상태를 확인한다.
### 요약 정보
* Struct 사용
* Struct 는 Stack 을 사용하고 Class 는 Heap 을 사용한다.
* Struct 는 GC 이 대상이 아니다.
* .net core 3.1 default stack size : 1536 KB
* 휘발성이 강한 경우 사용하면 좋다.
* 배열이나 container 에 넣는 경우 Heap 에 상주하게 된다.
* 이런 경우 pool 을 만들어 사용 한다.
* struct 사용시 stack 을 사용하므로 GC 대상에서 제외된다.
#### 기타 정보
* Gen 0 -> Gen 1 -> Gen 2 로 객체를 밀어 올리는 방법
* GC.Collect() API 를 사용하여 이동을 유도할 수 있다.
* 인위적으로 이동시킬수 있는 API 는 없다.
```csharp
// GC.Collect 를 호출하여 밀어올린다.
Object obj = new object();
Console.WriteLine($"generation: {GC.GetGeneration(obj)}"); // generation: 0
GC.Collect();
Console.WriteLine($"generation: {GC.GetGeneration(obj)}"); // generation: 1
GC.Collect();
Console.WriteLine($"generation: {GC.GetGeneration(obj)}"); // generation: 2
GC.Collect();
Console.WriteLine($"generation: {GC.GetGeneration(obj)}"); // generation: 2
```
* 인위적으로 객체를 GC 대상에서 제외 방법은 없다.
* GC 모니터링방법
* EventPipe 를 사용하거나 dotnet cmd tool 이 있다.
* .net core 3.1 프로세스는 은 "성능 모니터"의 ".NET CLR Memory" 카운터에 표시되지 않음
### 테스트 방법
* [EventPipe](https://docs.microsoft.com/ko-kr/dotnet/core/diagnostics/eventpipe) 로 GC event 로깅
* struct, class 생성 방식 변경하며 GC 로그 확인
* 인스턴스는 3억개 생성
### 테스트 코드
```csharp
// 배열 생성
public static void AllocIntArray()
{
for (int i = 0; i < Count; i++)
{
int[] x = new int[100];
}
}
// class instance 생성
public static void AllocClass()
{
for (int i = 0; i < Count; i++)
{
AllocClass inst = new AllocClass()
{
Number = i,
};
}
}
// struct instance 생성
public static void AllocStruct()
{
for (int i = 0; i < Count; i++)
{
// GC 이벤트 자체가 일어나지 않음
AllocStruct inst = new AllocStruct()
{
Number = i,
};
}
}
// list 에 class instance 삽입
public static void AllocClassInList()
{
List<AllocClass> list = new List<AllocClass>();
for (int i = 0; i < Count; i++)
{
AllocClass inst = new AllocClass()
{
Number = i,
};
list.Add(inst);
}
list.Clear();
}
// list 에 struct instance 삽입
public static void AllocStructInList()
{
List<AllocStruct> list = new List<AllocStruct>();
for (int i = 0; i < Count; i++)
{
AllocStruct inst = new AllocStruct()
{
Number = i,
};
list.Add(inst);
}
list.Clear();
}
```
### 테스트 결과
| |AllocIntArray|AllocClass|AllocStruct|AllocClassInList|AllocStructInList|
|---|---|---|---|---|---|
|Alloc 소요시간|7,536 msec|890 msec|65 msec|9,855 msec|1,192 msec|
|GC 발생 횟수|900|50|0|6|0|
|GC 소요 시간|326 msec|14 msec|0 msec|11,717 msec|0 msec|
|FragmentedBytes|3 MiB|7.5 MiB|0|69.5 MiB|0|
|HeapSizeBytes|4.8 MiB|8.6 MiB|0|12,357.8 MiB|0|
|테스트 후 GC.Collect시 발생 횟수|3|1|0|2|1|
|테스트 후 GC.Collect시 소요 시간|20 msec|8 msec|0 msec|2562 msec|218 msec|
* [GCMemoryInfo 구조체](https://docs.microsoft.com/ko-kr/dotnet/api/system.gcmemoryinfo?view=netcore-3.1)
* FragmentedBytes : 마지막 가비지 수집이 발생한 총 조각화를 가져옵니다. (heap 이 compact 되기전에 생존한 객체 사이에 빈공간의 총 크기)
* HeapSizeBytes : 마지막 가비지 컬렉션이 발생 한 총 힙 크기 (바이트)입니다.
'C#' 카테고리의 다른 글
c# method attribute 의 값을 해당 메소드에서 가져오기 (0) | 2022.03.15 |
---|---|
c# HttpClient (0) | 2022.03.15 |
c# channel (0) | 2022.03.15 |
object pool, object type pool (0) | 2022.03.11 |
GC EventPipe 모니터링 (0) | 2022.03.08 |
C# .net core 빌드 및 powershell 전송 (0) | 2022.03.01 |
c# stack size 확인 (0) | 2022.02.24 |
숫자 범위 추출 및 확장 (0) | 2021.11.03 |