-
[C#] C# 에서 상호 참조는 어떻게 해결이 될까?쾌락없는 책임 (공부)/C# 짜잘이 2022. 8. 6. 15:03반응형
개요
일단 C++의 unique_ptr을 보면 shared_ptr이 나오는 배경을 알 수 있습니다. 그리고 shared_ptr을 보게 되면 상호 참조 문제로 인해서 delete되지 않을 우려가 있기에 weak_ptr이 나오게 됩니다. 이런 이야기들은 포인터가 프로그래머에게 직접 보이는 C++의 이야기이고 포인터가 내부적으로 작동하고 밖에서는 볼 수 없는 C#과는 다른 이야기로 보이게 됩니다.
C#에서 delete 작업은 전적으로 GC에게 위임하고 있으며 이 GC의 동작을 알아보면 항상 '자동으로 쓰이지 않는 객체를 비할당' 해준다고 되어 있습니다. 그렇다면 C++의 shared_ptr에서 나오는 문제인 상호 참조 문제는 C#에서 어떻게 해결하고 있을까요?
C# GC는 mark and sweep 방식이다
일단 C#의 GC 관련해서 상호참조를 검색하다 이런 옛날 글을 보게 되었습니다. 아래 내용을 보면 C#의 GC는 mark and sweep 방식이기에 관리가 된다! 라는 내용입니다. 이를 통해서 C#에서는 상호 참조를 해결할 수 있다는 이야기가 있습니다. 그러면 mark and sweep 방식은 어떤 방식이라 상호참조를 해결하고 delete를 하는 것일까요?
Root 부터 사용하는 객체들을 타고 가면서 사용하는 객체들을 mark 하고 이후 mark 되지 않은 객체들을 전부 제거하게 됩니다.
위 내용이 mark ans sweep 를 간략하게 한 내용입니다. 여기서 root는 현재 사용하는 함수의 지역변수나 현 시점 클래스의 static 변수 등이 될 수 있습니다.
그럼 여기서 어떻게 상호 참조가 제거될까요? 여기서 1, 2가 서로를 참조하고 있을 때 1이 더이상 사용되지 않는 상황이 되었다고 합시다. 이 경우 GC가 한번 동작하면 2가 1을 참조하고 있으므로 1이 살아있을 수 있게 됩니다. 물론 2는 직접 사용하고 있으니 당연히 사라지지 않죠.
이후 2도 사용을 하지 않게 되면 mark 단계에서 1, 2는 더이상 mark되지 않고 이후 sweep 과정에서 둘 다 사라지게 됩니다. 이런 식으로 상호 참조중인 객체들이 delete되지 않는걸 피할 수 있게 되는 것이죠.
현재 C#의 GC는 뭘 사용할까
C#의 GC는 위 mark and sweep 방식에서 세대 구분이 추가된 알고리즘을 사용한다고 합니다. 그 외 생각할거는 추가적으로 없는 것 같네요.
유니티의 GC는 어떨까?
일단 유니티의 GC는 닷넷보다 구세대 버전을 사용하는것으로 알고 있습니다. 세대 구분 없고 GC 작동을 할 때 스레드도 따로 빼주지 않죠.
다행히 이것과 관련한 내용을 유니티 공식 블로그에서 볼 수 있었습니다. 제가 주로 사용하는 버전이 2019.4.21 버전이므로 딱 맞는 이야기가 됩니다.
요약해보면 유니티의 이전 GC는 Boehm-Demers-Weiser 방식이며 일단 프로그램이 멈춘 뒤 GC를 하는 방식이라고 합니다. 이후 점진적 GC를 도입하게 되었는데 동일한 방식이지만 점진적 모드로 GC 작업을 여러 슬라이스로 해 프레임이 확 떨어지는 상황을 최대한 나오지 않게 한다고 합니다. 뭐 근데 일단 우리가 알고싶은건 상호 참조일 경우 어떤식으로 해결을 할지를 알아보는거겠죠.
위 링크를 타고 들어가 Boehm GC의 소개 부분을 보면 mark and sweep 알고리즘을 사용한다고 나와 있습니다. 그러면 상호참조에 대한 걱정은 없겠네요!
추가 참고 자료
- GC의 Root는?
- 한국어로 되어 있는 C# GC와 관련한 이야기
반응형'쾌락없는 책임 (공부) > C# 짜잘이' 카테고리의 다른 글
[C#] is null vs == null (0) 2023.01.15 [C#] C# string vs stringbuilder, stringbuilder는 어떻게 이득이 될까? (0) 2022.08.06