.Net 리소스 관리에 대한 이해
.Net의 리소스를 관리하려면 가비지 수집기(Garbage Collector, GC)의 동작 방식을 알아야 한다.
C#은 C++과 다르게 Heap 메모리에 적재시킨 인스턴스 영역을 해제 시킬 필요가 없다.
왜냐하면 가비지 수집기가 알아서 해제 시켜주기 때문이다.
C++을 먼저 접했던 나는 조금 꺼림찍한 내용이었다.
자 그럼 가비지 수집기의 동작방식을 알아보자.
가비지 수집기는 관리되는 메모리(managed memory)를 다룬다.
그래서 아래의 3가지와 같은 문제를 개발자는 부담을 줄일 수 있다.
1. 메모리 누수(leak)
2. 댕글링 포인터(dangling pointer)
3. 초기화되지 않는 포인터
그렇다면 개발자는 마음대로 객체를 할당하고 사용만 하면 되는 것인가??
반은 맞고 반은 틀리다.
이유를 들어보자
1. 가비지 수집기는 데이터베이스 연결, GDI+ 객체, COM 객체, 시스템 객체 등과 같은 비관리 리소스는 여전히 개발자가 관리해야 한다.
2. EventHandler나 delegate 등도 참조는 되어있지만 사용하지 않는 객체들이 불필요하게 메모리에 남을 수도 있다.
자 위와 같은 종류의 객체들은 어떻게 관리하고 사용해야 할까??
.Net framework는 IDispose 인터페이스를 제공하고 개발자는 표준 Dispose 패턴만 활용하면 문제를 해결할 수 있다.
IDispose패턴을 활용하는 방법은 ITEM17에 다시 포스팅하겠다..
자 다시 돌아와서 가비지 수집기는 어떤 원리로 메모리를 관리하는 것일까?
혹시 마크/콤팩트(Mark/Compact) 알고리즘이라고 들어봤는가?
알면 해당 포스팅은 그냥 넘기면 될 것 같다.
마크/콤팩트 알고리즘은 여러 객체 사이의 관계를 파악하고 사용하지 않는 객체는 자동으로 제거하는 알고리즘이다.
여러 객체사의 관계란?
Class A는 Class B를 상속한다.
Class A의 필드 변수에 Class C라는 인스턴스를 가지고 있다.
이런 부분을 관계라고 생각하면 될 것이다.
본인은 WPF에서 MVVM 패턴을 사용하므로 MainViewModel으로 예시를 들겠다
1. MainViewModel안에는 Test1Model, Test2Model을 가지고 있다.
2. 사용자가 Test2Model에서 일련의 연산 수행시켜
Class C라는 것을 할당하여 실행했다.
3. 실행이 끝난 후 Class C는 그대로 남아있다.
4. 가비지 수집기가 최상단 MainViewModel 객체의 관계를 탐색한다.
5. Class C는 Test1Model에서 계속 사용하고 있는가?? -> No
6. Heap 메모리는 Compact 됐다.
그림이 많이 조잡하지만 이해해주길 바란다.
마크/콤팩트 알고리즘의 자세한 내용은 google에서 조금 더 탐색해보길 바란다...
가비지 수집기는 수집 과정을 최적화 하기 위해서 세대(generation)이라는 개념이 도입되어있다.
내용이 너무 많으므로 간단하게 설명하겠다.
세대는 가비지 수집기가 한번 수행할 때마다 증가하게 되어있다.
예를 들어 위의 상황을 들어보자
1. 프로그램 실행 시
MainViewModel : 0 세대
Test1Model : 0 세대
Test2Model : 0 세대
2. 사용자가 Test1ViewModel에서 연산을 시켜서 Class C를 추가했을 때
MainViewModel : 0 세대
Test1Model : 0 세대
Class C : 0 세대
Test2Model : 0 세대
3. 가비지 수집 수행
MainViewModel : 1 세대
Test1Model : 1 세대
Class C : 제거
Test2Model : 1 세대
4. 사용자가 Test1ViewModel에서 연산을 다시 실행
MainViewModel : 1 세대
Test1Model : 1 세대
Class C : 0 세대
Test2Model : 1 세대
5. 가비지 수집 수행
MainViewModel : 2 세대
Test1Model : 2 세대
Class C : 제거
Test2Model : 2 세대
이해되었는가?
가비지 수집기로부터 살아남으면 세대가 증가하는 것이다.
그럼 Global 변수와 멤버 변수는??
자연히 세대가 높은 객체가 될 것이다.
자 그럼 세대가 큰 것과 작은 것의 차이는 무엇일까?
바로 가비지 수집기가 탐색/수집하는 대상 선정의 차이다.
즉
1세대 객체는 가비지 수집 10번에 한번 탐색 대상
2세대 객체는 가비지 수집 100번에 한번 탐색 대상
이라는 뜻이다. 10번, 100번은 대략이다 자세한 내용은 스스로 찾아보길 바란다.
자 여기까지가 가비지 수집기의 간단한 작동 이론이다.
결론
1. 가비지 수집기 덕분에 .Net Framework 개발자들은 메모리 관리에 신경을 덜 써도 된다.
2. 가비지 수집기는 마크/콤팩트(Mark/Compact) 알고리즘, 세대(Generation) 최적화 기법을 사용하여 가비지 수집을 한다.
3. 데이터베이스 연결, GDI+ 객체, COM객체, 시스템 객체 등 과같은 비관리 리소스는 IDispose 패턴을 사용하여 메모리 해제 기능을 만든다.
4. eventhandler, delegate 등 참조는 되어있지만 사용하지 않는 객체들이 불필요하게 메모리에 남을 수도 있다.
'Program > Effective C#' 카테고리의 다른 글
Effective C# ITEM14 (0) | 2020.10.28 |
---|---|
Effective C# ITEM13 (0) | 2020.10.28 |
Effective C# ITEM 12 (0) | 2020.10.26 |
Effective C# ITEM10 (0) | 2020.10.21 |
Effective C# ITEM9 (0) | 2020.10.21 |