좀 길지만 결론은 컴파일러만 알수 있다 입니다.
먼저 Go 언어는 C와 마찬가지로 **스택(stack)**과 힙(heap) 메모리를 사용합니다. 스택은 각 고루틴(goroutine)에 독립적으로 존재하며, 힙은 그 외의 모든 메모리를 포함합니다.
스택 vs 힙
- 스택: 함수 내 지역 변수들이 저장되며, 함수가 호출될 때 메모리가 할당되고 반환될 때 해제됩니다. 스택은 “자동 정리(self-cleaning)” 특성을 가지고 있어 성능이 좋습니다.
- 힙: 가비지 컬렉터(Garbage Collector, GC)가 관리하며, 스택보다 느리고 프로그램 전체에 영향을 줄 수 있는 지연(latency)을 유발할 수 있습니다.
변수가 스택에 할당되는지 힙에 할당되는지 결정
Go 컴파일러는 변수의 사용 방식에 따라 스택 또는 힙에 할당 여부를 결정합니다. 이 과정은 **탈출 분석(Escape Analysis)**을 통해 이루어집니다.
탈출 분석의 주요 원칙
- 변수가 함수 반환 후에도 참조될 가능성이 있다면, 해당 변수는 힙에 할당됩니다.
- 변수가 너무 커서 스택에 저장하기 어렵다면, 힙에 할당됩니다.
- 컴파일 시점에 변수의 크기를 알 수 없다면, 힙에 할당될 가능성이 높습니다.
공유 방향에 따른 메모리 위치
- 공유 다운(Sharing Down): 포인터를 함수로 전달하는 경우, 일반적으로 변수는 스택에 남아 있습니다.
- 공유 업(Sharing Up): 포인터를 반환하거나 참조를 반환하는 경우, 일반적으로 변수는 힙으로 "탈출"합니다.
예제 코드 분석
1. 스택에서의 동작
go
func main() {
n := 4
result := square(n)
fmt.Println(result)
}
func square(x int) int {
return x * x
}
square
함수 호출 시n
과x
는 각각 스택 프레임(stack frame)에 저장됩니다.- 함수가 종료되면 해당 메모리는 유효하지 않은 상태로 간주되지만, 새로운 호출 시 재사용됩니다.
2. 힙으로 탈출하는 경우
go
func main() {
p := answer()
fmt.Println(*p)
}
func answer() *int {
x := 42
return &x
}
answer
함수에서x
의 주소를 반환합니다. 이 경우x
는 함수 종료 후에도 참조되므로 힙에 저장됩니다.
컴파일러에게 확인하기
Go 컴파일러가 변수들을 어디에 할당하는지 확인하려면 다음 명령어를 사용할 수 있습니다:
bash
go build -gcflags="-m"
- 이 명령어는 컴파일러의 최적화 결정을 출력하며, 어떤 변수가 힙으로 탈출했는지 보여줍니다.