들어가기 전
Java를 공부하기 시작하면서 JAVA가 어떻게 메모리를 사용하는지 확인하게 됐다. JAVA는 Static, Stack, Heap 이렇게 세 종류의 구분을 두어 메모리를 관리했고 이 부분은 다음에 다시 언급할 기회가 있을 듯하다.
그렇다면 Go는 어떨까? JAVA에서의 Static은 Class를 주로 저장하는 듯한데 Class가 없는 Go는 어떨까?라는 의문을 가지고 Go가 메모리를 사용하는 방법을 살펴보았다.
Go의 메모리 할당
우선 Class가 없어서 인지는 모르겠지만 Go는 Stack과 Heap 두 종류로 구분돼 있음을 확인했다..!
그렇다면 이 두 메모리를 어떻게 사용하고 있을까? 같이 알아보도록 하자!
Stack
Stack을 사용할 때의 코스트는 적다.(가볍다.)
Stack에 메모리를 사용하는 방법은 할당과 해제 단 두 개이다. --> 그렇기에 가볍다.
Stack에 할당할 수 있는 것은 변수의 수명과 메모리 사용량이 컴파일 시에 확정되는 경우이다.
Heap
Heap을 사용할 때의 코스트는 크다.(무겁다.)
Heap에 할당하는 것은 동적으로 메모리를 할당 하는 것 == 가비지 컬렉터가 할당된 개체를 확인해야 하는 것 --> 그렇기에 무겁다
Go가 Stack, Heap 할당을 알아차리는 방법
이스케이프 분석
Go 컴파일러는 이스케이프 분석(escape analysis)라는 기술을 사용하여 Stack, Heap 할당을 나눈다.
이 이스케이프 분석은 컴파일러가 변수의 범위를 추적해 수명이 특정 범위로 한정될 수 있거나 컴파일 시에 메모리의 크기가 확정될 경우 Stack할당을 지시한다.
포인터를 조심해야 하는 이유
그러나 조심해야 할 부분이 있다. 포인터를 사용하면 대부분의 경우 힙 할당이 되어 버린다는 게 그것이다.
Go의 경우 Duff's devices라는 기법을 사용해 메모리 복사 등의 일반적인 작업에 매우 효율적인 어셈블러 코드를 사용한다.
함수의 인수나 메소드 리시버 역시 값을 복사하는 것이 가벼운 처리이다.
포인터를 사용한다면 nil 체크를 하는 만큼의 복잡도가 올라가고 값을 복사함으로써 메모리에서 CPU 캐시 적중률 또한 오른다.
같은 이유로 슬라이스와 문자열 또한 조심하여야 한다.
마무리
어려운 용어도 많고 모르는 기술도 많고 업데이트가 활발한 언어다 보니 이전 버전과 달라진 점도 발견됐다..
이러한 부분을 놓치지 않고 항상 관심을 가지도록 노력해야겠다.
참조.
https://syntaxsugar.tistory.com/entry/GoGolang%EC%9D%B4-%EB%8A%90%EB%A0%A4%EC%A7%80%EB%8A%94-%EC%9D%B4%EC%9C%A0
https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/
https://jacking75.github.io/go_heap-allocations/
'Study > Go' 카테고리의 다른 글
[Go] 고루틴(GoRoutine) 심화(1) - 합류지점 (0) | 2022.04.16 |
---|---|
[Go] Go에서 DI는(1) - DI란? (0) | 2022.04.09 |
[Go] Heap 자료구조를 사용하는 우선순위 큐 (Priority Queue) (0) | 2021.10.04 |
[Go] 클로저(Closure)를 활용하여 생성기(generator) 제작 (0) | 2021.10.02 |
[Go] 고루틴(Goroutine) 알고쓰기(2) - Block 처리방식, Channel (0) | 2021.09.17 |