7feee109dfe70fb759b7e79804c6052fab7a32d51fbad6f859d0be21b10573f6c4b7ec43c70d2d469cdf562d862e8fd9c93d90c683021deb9c9fad4c1c1a77d055278b7e59a12514a86d8c1988571907286b8d91b572a4c8b8ba59795af73652463e911d20c743102169b83267f02212f1a4d48fb1ef8415c4971370616e475b340b2fc3394268bdf14adc46359b9d419a526426915902f5c05f3af62d54e30c34460448771a25


7.GC(가비지 컬렉션)


- C#에는 힙 메모리와 스택 메모리라는 2가지 메모리 관리 풀이 있음. Stack(스택) 메모리는 주로 작고 (지속시간이) 짧은 데이터를 저장하는데 사용되고, 힙 메모리(Heap)는 더 크고 오래 지속되는 데이터를 저장하는데 주로 사용됨. C#의 변수는 스택 또는 힙 메모리에만 메모리를 할당함.

변수는 스택 메모리 또는 힙 메모리에 저장됨.

(즉 값 형식(int,float,struct)는 주로 스택에, 참조 형식(class,array,string)은 힙에 저장됨.)


변수가 더 이상 활성화되지 않으면 해당 변수가 차지하는 메모리는 더 이상 필요하지 않기때문에, 해당 메모리는 메모리 풀로 회수하여 다시 사용하는데,

이를 메모리 회수 작업이라고 함. 스택의 메모리는 빠르게 회수되지만, 힙의 메모리는 해당 메모리가 여전히 사용중으로 표시되기때문에 빠르게 회수되지 않음.

더 이상 사용되지 않는 메모리는 GC 시점에서만 회수됨,

(C#의 가비지 컬렉터는 사실 메모리 압력[memory pressure]과 힙에 할당된 메모리의 양에 따라 동적으로 작동하고, 이러한 패턴에 따라 GC가 트리거되지만, 꼭 이렇게까지 알 필요는 없음)


가비지 컬렉팅은 주로 힙에 메모리를 할당하고 회수하는 것을 말함.

C#에서는 일정한 간격으로 힙 메모리에서 GC 연산을 수행함.


7-1 GC 관련한 문제점


1. GC 작업은 매우 이벤트 집약적인 작업으로, 힙 메모리에 변수나 참조가 많을 수록 트래버스 검사 작업이 느려져서 어플리케이션이 느리게 실행됨.

예를 들어 게임으로 따지자면 GC작업중 프레임 감소가 일어날것임

2. GC 연산은 '메모리 단편화(Memory Fragmentation)'를 생성함. 메모리 단위가 힙에서 할당될 때 그 크기는 저장된 변수의 크기에 따라 달라짐. 힙에서 메모리를 회수할때 힙 메모리가 조각난 셀로 분할됨

(물론 .Net의 GC는 세대별 가비지 컬렉션을 통해 메모리 단편화를 줄이는 최적화 기법을 사용함. .NET은 GC는 세대(Gen) 으로 메모리를 관리하고, 적정한 세대에 따라 GC를 수행 후, 단편화를 최소화함)

(즉, 총 용량 크기는 고정되어있지만, 셀 메모리는 더 작아진다는 것. 즉 집에서 방을 나누는데 방이 점점 작아져서 적절한 방에 들어갈 수 없다는것)


7-2 GC 트리거 타이밍


1. GC는 힙에 메모리를 할당하는 만큼 메모리가 부족할때, 트리거 됨.

2. GC는 자동으로 트리거 되고 플랫폼마다 빈도가 다름.

3. GC 트리거 타이밍을 강제할 수 있음.

(GC.Collect()를 사용하면 되지만, 일반적으로 이는 권장하지 않음.)


7-3 GC를 피하는 방법


1. 임시 변수의 사용을 줄이고, 공용 객체 사용, 캐싱 매커니즘을 활용(함수 외부에 컨테이너 정의하고 사용할때만 수정)

2. new 횟수를 줄임. 즉 객체 생성을 최소화함.

3. 많은 수의 문자열 집합의 경우 String 대신 StringBuilder를 사용함.

4. List, StringBuilder등 확장 컨테이너를 사용할 경우 컨테이너를 정의할때, 저장 변수의 메모리 크기에 따라 저장 공간을 정의하며 컨테이너를 확장하는 작업을 줄여야함.

5. 코드로직으로 최적화를 해야함. 성능을 활용해야하는 특정 순간에 GC가 발생하지 않도록 시도함

(GC.TryStartNoGCRegion이 그 예시이지만 권장되지 않음)

6. 오브젝트 풀, 주로 자주 사용되는 오브젝트를 생성할때 자주 사용되는 메모리 관리법. 각 오브젝트를 생성하는데, 드는 시스템 오버 헤드를 줄이는 것이 목적.

7. Boxing(박싱) 및 언박싱(UnBoxing)의 필요성을 줄임. 일반적으로 제네릭을 사용하는것이 좋음.

(Boxing과 UnBoxing은 값 형식, 객체 형식또는 해당 값 형식이 구현하는 인터페이스 유형으로 변환하는 프로세스)


-일반적으로 GC를 수동 조작하는 것은 C#에서 권장되지 않음.


8. Struct(구조체) 및 Class(클래스) 차이


1. 구조체는 값 형식, 클래스는 참조 형식

2. 구조체는 스택에 존재하고, 클래스는 힙에 존재함.

3. 구조체 변수와 클래스 객체는 구조체는 값으로, 클래스는 참조로 전달됨.

클래스 객체는 참조, 즉 포인터로 전달되므로 함수에서 파라미터 값을 변경해도 구조체 객체의 값은 변경되지 않고, 클래스 객체의 값은 변경됨.


4. C#에서 구조체 유형을 정의할 때는 멤버를 초기화 할 수 없기때문에 구조체 변수를 정의할때는 변수의 모든 멤버를 자체 할당으로 초기화해야함.

5. 클래스의 경우 클래스를 정의할 때 멤버변수를 초기화할 수 있으므로 객체를 정의할 때 객체 자체에 이미 초기값이 있고, 개별 변수에 값을 직접 할당할 수 있음.


6. 구조체는 매개 변수가 없는 생성자를 정의할 수 없지만 클래스는 가능함.

7. 구조체를 선언한 후, 새 연산자를 사용하거나 new 키워드를 사용하지 않고, 생성된 객체를 만들 수 있음. new를 하지 않으면 필드가 할당되지 않은 상태로 유지되기에 모든 필드가 초기화 될때까지 객체(Object를 사용할 수 없음)

8.구조체는 소멸자를 정의 할 수 없지만 클래스는 가능함

9. 구조체는 상속할 수 없지만 클래스는 상속할 수 있음.

10. 구조체는 static으로 수정할 수 없지만, 클래스는 static 할 수 있음(Static 구조체는 존재하지않음)


8-1. 클래스와 구조체는 언제 어떻게 써야하는가?


구조체는 스택 값 형식으로, 스택은 힙보다 액세스 속도가 빠르고, 용량이 작아, 가벼운 객체에 적합

객체가 데이터 모음일 경우 우선순위는 구조체를 선택하는 것.


클래스는 참조 형식으로 실제 객체는 힙에 저장되고, 힙은 용량이 크고, 속도가 느린 객체에 적합하고,

힙은 스택 보다 더 큰 메모리를 공간을 제공하므로 더 많은 객체를 저장할 수 있음.

대상이 상속 및 다형성 기능을 필요로 하는 경우 클래스를 사용함.