Home .NET GC 정리(2)
Post
Cancel

.NET GC 정리(2)

.NET GC 정리(2)

인터넷 여기저기 흩어져있는 GC 관련정보 정리 2탄.

틀린게 있다면 말해주세요.


GCHeapAffinize

SGC (Server GC)는 논리 코어갯수만큼의 힙을 가지고 있고 멀티코어에서는 병렬처리가 가능하기때문에 WGC (WorkStation GC) 보다 처리량이 월등하다.

Heap Affinize는 힙과 코어를 1:1로 대응시켜주는 기능이다. (힙a는 쓰레드 A가 처리해!)

힙의 갯수가 논리코어의 갯수와 일치하기때문에 각 코어는 GC 동안 자기힙만 계속해서 처리가 가능하다는 뜻이다. 온전히 자기힙만 처리하므로 성능이 더 좋아질것은 당연하다.

그러나, 힙과 코어가 1:1 대응되어 일을 하던 와중에 코어가 다른 작업에 정신이 팔린다면? (시스템에 우선순위가 매우높은 IO 작업같은게 발생했다고 치면..)

그 힙은 계속해서 정리가 되지 못한다는 사실이다. SGC는 여러 힙이 동시에 정리되지만, 마지막 힙의 정리가 끝날때까지 STW(Stop The World)가 계속된다.

이로인해 월등히 빠르다고 생각했던 0, 1세대 GC도 계속해서 멈춰있을 수도 있다는 사실이다.

이를 방지하기 위해 GCNoAffinize 옵션을 사용해서 세팅을 바꿀 수 있다.

GC.AllocateUninitializedArray

POH를 사용하기 위한 API.

.NET 으로 소켓서버를 구현하면 SAEA(SocketAsyncEventArgs)에 버퍼를 세팅하고 이 버퍼는 메모리풀을 사용하곤 한다. (ArrayPool 과 같은..)

SAEA는 내부적으로 Overlapped로 이루어졌다는 건 모두가 아는 사실. Overlapped에 세팅된 버퍼는 완료 이벤트가 통지되기 전에는 손대면 안되고, 손대면 안되는 주체는 GC도 포함이다.

GC의 컴팩션 단계에서는 메모리의 주소가 바뀔수 있기때문에 SAEA에 세팅된 버퍼는 자동으로 pinning이 된다. 그리고 이는 GC를 방해하는 요소가 된다. (방 청소하려는데 고정된 탁자가 있는것 마냥..)

버퍼풀을 쓴다고 pinning이 해결되는건 아니다. 이미 pinning이 된 버퍼들을 돌려쓰는 것 뿐.

그래서 .NET 5부터 POH가 나온것이다. POH는 사용자가 직접 할당하는 영역이다. GC.Allocate~ 를 사용해서 POH에 메모리를 할당할 수 있고. POH는 2세대 취급이라 컴팩션을 하지않으므로 GC에 악영향도 가지않는다. 메모리 파편화도 줄일 수 있게되고.

결론적으로 가능하다면 소켓버퍼와 같이 pinning된 메모리가 필요한 경우 POH를 쓰는게 무조건 이득이라는 점.

참고로, Pinned Pool에 대한 예시는 ASP.NET Kestral 소스코드 에서 찾아볼 수 있다.


0, 1세대의 비정상적인 STW와 pinning으로 인한 파편화 이슈는 업무 중에 실제로 발생했었다. 팀원, 팀장님의 캐리를 받으며.. 몇 없는 자료에 다 영어 뿐이라 머리가 아팠다. 까먹지 않기위해 정리한다. 이 글을 보실지 모르겠으나 팀원분들 감사합니다.

This post is licensed under CC BY 4.0 by the author.

.NET GC 정리(1)

.NET 8 변경점