-
21장. 관리 힙과 가비지 수집CLR Via C# 2022. 7. 13. 17:47
리소스(타입)에 접근하기 위해 필요한 일련의 절차
- 리소스를 대표하는 타입을 위해 메모리 공간을 할당한다. (C#에서는 일반적으로 new 연산자를 이용하게 된다.)
- 리소스의 초기 상태를 설정하기 위해서 메모리를 초기화 하고 리소스를 가용 상태로 만든다. 타입의 인스턴스 생성자(instance constructor)는 초기 상태를 설정하기 위한 용도다.
- 타입의 멤버들을 이용하여 리소스를 사용한다.(필요한 만큼 반복적으로)
- 리소스를 정리하기 위해서 상태를 변경한다.
- 메모리를 해제한다. 이 과정은 가비지 수집기가 단독으로 수행한다.
관리 힙에 리소스 할당하기
C#에서는 모든 객체가 관리 힙에서 할당되어야 한다. 프로세스가 초기화되면 관리 힙으로 사용할 주소 공간 영역을 우선 할당한다. C#에선 객체생성을 위해 new를 통해 특정 타입의 필드를 고려해 객체를 위해 할당할 크기가 관리 힙 내에 여유가 있는지 확인한다. 여유가 있다면 타입 생성자를 호출하고 new 연산자는 객체의 참조를 반환하게 된다.
관리 힙을 통해 참조의 지역성을 높혀 객체 사용의 속도를 빠르게 할 수 있다. 하지만 관리 힙의 매모리 공간이 무한정이 아니기 때문에 더 이상 사용하지 않는 객체를 힙으로부터 삭제하기 위해서 가비지 컬렉션(Garbage Collection, GC) 이라고 얄려진 기법을 채용하였다.
가비지 컬렉션 알고리즘
응용프로그램이 new 연산자를 호출하여 새로운 객체를 생성하려 할 때, 관리 힙의 영역 내에 충분한 주소 공간이 남아있지 않을 수도 있다. 이처럼 여유공간이 부족할 경우 GC를 수행하게 된다.
C#은 참조 추적 알고리즘을 사용한다. 이러한 알고리즘은 참조 타입의 변수만을 대상으로 하는데, 이는 값 타입의 변수는 값 자체를 직접 포함하는 반면 참조 타입 변수들 만이 관리 힙 내에 객체를 참조하기 때문이다. 참조 타입의 변수는 클래스 내의 정적 혹은 인스턴스 필드, 메서드의 매개변수, 지역 변수 등으로 다양하게 사용될 수 있다. 이 같은 참조 타입 변수를 루트(root) 라고 부른다.
GC는 검토하려는 객체에 스레드가 접근하거나 상태를 변경하지 못하게 하기 위해서 가장 먼저 프로세스 내의 모든 스레드를 일시 정지시킨다. 그리고 마크 단계(marking phase) 라고 불리는 과정을 수행한다. 이는 도달 가능한(참조된) 객체를 마크하고 도달 불가능한 객체는 마크하지 않는 과정이다. 이제 어떤 객체를 유지해야 하고 어떤 객체를 삭제해야 하는지 알고 있다. 다음으로 GC는 컴팩트 단계(compacting phase) 를 수행한다. 컴팩트 단계는 마크된 객체들이 사용 중인 메모리를 힙의 시작 지점 쪽으로 옮겨서 모든 살아남은 객체들이 연속된 메모리 공간상에 위치하도록 한다.
GC작업 이후에도 메모리를 확보할 수 없거나 새로운 프로세스 내부에 GC 세그먼트를 할당할 주소 공간이 더 이상 남아있지 않다는 것은 결국 프로세스가 사용할 수 있는 메모리가 더 이상 남아 있지 않다는 것이다. 이러한 상황에서 메모리를 할당 받기 위해서 new 연산자를 호출하면 결국 OutOfMemoryException 예외가 발생하게 된다.
또한 GC는 세대를 고려한 GC(generational GC) 다. 세대는 0, 1, 2 세대가 있다. 0세대가 처음 생긴 객체가 있고 GC 이후 0세대에 남아있는 객체들은 1세대로 승격한다. 또한 GC는 각 세대별로 허용 범위를 넘어서야 발생한다. 즉, 세대가 증가할수록 해당 객체는 더 오래 생존한다.
'CLR Via C#' 카테고리의 다른 글
20장. 예외와 상태 관리 (0) 2022.06.30 19장. Null 값 타입 (0) 2022.06.21 18장. 사용자 정의 특성 (0) 2022.03.16 17장. 델리게이트 (0) 2022.03.09 16장. 배열 (0) 2022.02.28