티스토리 뷰
[Effective C# Item 11] .NET 리소스 관리에 대한 이해
.NET 의 가비지 콜렉터의 동작 방식을 잘 알아야 하고, 비관리 리소스를 어떻게 다룰지에 대해서도 정확히 이해해야 한다.
가비지 콜렉터
가비지 콜렉터는 관리되는 메모리를 관장하며 네이티브 환경과는 다르게 메모리 누수, 댕글링 포인터, 초기화되지 않는 포인터, 다른 메모리 관리 문제를 개발자들이 직접 다루지 않도록 자동화해준다. 가비지 콜렉터가 메모리 전반적으로 관리해주기 때문에 개발자는 응용프로그램의 구조를 단순하게 유지할 수 있다.
가비지 콜렉터의 마크/콤팩트(Mark/Compact) 알고리즘은 여러 객체 사이의 연관 관계를 효율적으로 파악하여 더 이상 사용되지 않는 객체를 자동으로 제거한다.
가비지 콜렉터가 수행되면 관리 힙(Heap)에 대하여 콤팩트(compact)작업을 수행한다. 콤팩트 작업이란 사용 중인 객체들을 한쪽으로 차곡차곡 옮겨서 조각난 비어있는 메모리를 단일의 큰 메모리 공간으로 만드는 과정을 말한다.
비관리 리소스
가비지 콜레터와는 별도로 비관리 리소스는 여전히 개발자가 직접 관리해줘야 한다. .NET 프레임워크는 비관리 리소스의 생명주기에 대해서도 개발자가 쉽게 관리할 수 있도록 finalizer와 IDisposable 인터페이스라는 두 가지 메커니즘을 제공한다.
finalizer는 비관리 리소스에 대한 해제 작업이 반드시 수행되도록 하는 방어적인 메커니즘인데 finalizer는 단점이 많기 때문에 이보다는 IDisposable 인터페이스를 통해서 적시에 비관리 리소스를 해제 시켜주는 것이 좋다.
finalizer를 사용하면 성능이 나빠진다. finalizer를 포함하고 있는 객체를 사용하면 가비지 콜렉터 과정이 더 길어지고 finalizer를 포함하고 있는 객체를 가비지로 판단한 경우에 이후에 finalizer를 호출해야 하기 때문에 객체가 점유하고 있는 메모리 공간을 해제하지 못한다. 문제는 이 호출되는 시기를 아무도 알 수 없다는 것이다.
따라서 finalizer가 필요한 구조를 회피하고 finalizer가 필요하더라도 부정적인 영향을 최소화하는 방법을 생각해야 한다.(뒤에 관련 내용이 나온다고 한다.)
세대(generation)
.NET의 가비지 콜렉터는 가비지 수집 과정을 최적화하기 위해 '세대'라는 개념을 사용한다. 이 '세대'라는 개념은 가비지가 될 확률이 높은 객체를 더 빠르게 찾아낼 수 있게 한다.
가비지 콜렉터의 객체 세대는 다음과 같이 동작한다. 우선 가비지 콜렉터가 수행된 이후 임의의 객체가 생성됐다고 하자. 이 객체를 0세대 객체라고 한다. 여기서 가비지 콜렉터가 동작됐다고 하자. 만약 기존에 생성한 객체가 여전히 쓰인다면 이 객체를 1세대 객체라고 한다. 그리고 두 번 이상 가비지 콜렉터가 수행되었음에도 살아남은 객체가 있다면 그 객체를 2세대 객체라고 한다. 대체로 0세대 객체는 가장 최근에 생성된 객체이고 1세대, 2세대 객체는 멤버 변수나 전역 변수일 것이다.
[0세대 가비지 컬렉션 동작 원리]
[1, 2세대 가비지 컬렉션 동작 원리]
세대에 따른 가비지 콜렉터 동작 방식
가비지 콜렉터는 1세대와 2세대 객체에 대해서는 제한적으로 가비지 콜렉터를 수행한다. 가비지 콜렉터가 수행되면 우선 0세대 객체에 대해서만 수행되고 대략 10번에 한 번 꼴로 추가적으로 1세대 객체에 대해서 가비지 컬렉션이 수행되고 약 100번에 한 번 꼴로 2세대 객체를 포함한 모든 세대의 객체를 대상으로 가비지 컬렉션을 수행한다.
다소 어려운 부분에 다다른 것 같다.
.NET 리소스 관리 부분은 사실 몰라도 코딩을 하는데 큰 지장이 없을 수 있다. 하지만 이제 내가 단순한 코딩이 아닌 효율적인 코딩을 지향한다면 절대로 간과해서는 안 되는 부분일것 이다.
참조 - Effective C# <강력한 C# 코드를 구현하는 50가지 전략과 기법, 이펙티브>, 빌 와그너, 김명신, 한빛미디어
- 공돌창고 :: C#/.NET 세대별 가비지 컬렉션(Garbage Collection) 동작 설명 <https://hijuworld.tistory.com/41>
'Programming > Effective C#' 카테고리의 다른 글
[Effective C# Item 13] 정적 클래스 멤버를 올바르게 초기화하라 (0) | 2020.10.18 |
---|---|
[Effective C# Item 12] 할당 구문보다 멤버 초기화 구문이 좋다 (0) | 2020.10.18 |
[Effective C# Item 10] 베이스 클래스가 업그레이드된 경우에만 new 한정자를 사용하라 (0) | 2020.10.18 |
[Effective C# Item 9] 박싱과 언박싱을 최소화하라 (0) | 2020.10.18 |
[Effective C# Item 8] 이벤트 호출 시에는 null 조건 연산자(?.)를 사용하라 (0) | 2020.10.18 |