프로그래밍 업계에서 성능이 좋은 언어라고 말하면 go vs rust 가 나옵니다.
절대 python, javascript가 빠르다고 하는 사람은 없지요.
그런데 Go는 알려진대로 Garbage Collector가 있는 관리 언어입니다.
Rust는 알려진대로 Garbage Collector가 없는 비관리 언어입니다. 비슷한 비관리 언어로 C, C++이 있지요.
관리 언어는 가비지 컬렉터로 인해 성능이 잘 나오지 않기 때문에 비관리 언어가 성능측면에서 좋다라는 말을 많이들 합니다.
그런데 그럼 Go는 관리언어인데 왜 인식이 좋은걸까요?
Go로 개발된 기가막힌 솔루션에는 제가 알기로는 Docker와 Kubernetes가 있습니다.
가비지 컬렉터가 있어서 느리다. 라는 것은 아무래도 가비지 컬렉션이 발생할 때의
- STW (Stop-The-World)
- Memory Compaction
두 가지 때문일꺼라고 생각합니다.
가비지컬렉션이 동작할 땐 프로세스 안의 모든 스레드가 일시정지하는 순간이 오고 수집할 객체가 많을 수록 이 시간이 크다고 합니다.
그리고 가비지 컬렉션이 발생한 후에는 Heap에 메모리 할당을 빠르게 하기 위해 수집된 객체들이 빵꾸낸 자리를 메꿔서 매모리들을 재정렬시킨 다음 순차적으로 메모리 할당을 하기 시작한다고 하죠.
물론! 이 과정에서 당연히 객체의 Heap 메모리 주소는 바뀌게 될 것입니다.
관리언어가 메모리를 할당하는 속도가 빠른 이유는 이 메모리 압축이 발생하여 객체를 할당할 시작주소를 이미 알고 있기 때문입니다.
저는 비관리 언어는 잘 모르지만 메모리를 할당하는 시간은 관리언어에 비해서 느리다고 알고 있습니다. GC가 없이 수동해제하기 때문에 개발자가 원할때 바로 제거할 수 있어서 그런 면에선 좋지만요.
이런 것들을 피하기 위해 가비지 컬렉션이 빈번하게 발생하는 0,1 세대가 아니라 2세대에 큰 객체를 할당하여 Full GC만 이용하도록 하면 서비스의 휴지기에 수집을 하여 사람들이 불편을 느끼지 않을 수 있다고 합니다. (여기서 0,1,2 세대는 .NET 가비지 컬렉션 기준이고, 다른 언어는 용어나, Generation 수가 다를 수 있습니다.)
또한 최근 .NET 5부터 등장한 POH같은 것으로 가비지 컬렉션의 영향을 받지 않는 Heap에 메모리를 할당하는 것입니다.
관리언어가 Heap에 대해 개발자가 직접 해제를 할 수 없어서 느리다고 알려져 있는데, 다른언어는 모르겠으나 적어도 .NET에서는 가비지 컬렉션이 메모리를 할당하고 제거하는 Heap은 GC Heap이라고 불리지요. (사실 가비지 컬렉터가 메모리를 제거만하지 할당을 한다는 개념은 모르는 사람도 있긴 했습니다.)
즉, GC Heap이 아닌 Heap에 할당하여 개발자가 직접관리하면 되는 부분이라고 생각합니다. 비슷한 맥락으로 가비지 컬렉터가 접근하지 못하는 Heap은 static 객체들이 할당되는 High Frequency Heap도 있고, Literal String이 저장되는 Intern Pool도 존재합니다.
그 밖에도 가비지 컬렉터가 접근하지 못하는 여러 Heap이 많은 것으로 알고 있습니다.
같은 하드웨어 위에서 동작하니만큼 똑같은 CPU의 IO를 탈 것이고 아키텍쳐도 같을 텐데 go만 성능적으로 우수하다는 인식이 생긴 이유는 무엇일까요?