Go lang에서 스택 vs 힙 차이

좀 길지만 결론은 컴파일러만 알수 있다 입니다.

먼저 Go 언어는 C와 마찬가지로 **스택(stack)**과 힙(heap) 메모리를 사용합니다. 스택은 각 고루틴(goroutine)에 독립적으로 존재하며, 힙은 그 외의 모든 메모리를 포함합니다.

스택 vs 힙

  • 스택: 함수 내 지역 변수들이 저장되며, 함수가 호출될 때 메모리가 할당되고 반환될 때 해제됩니다. 스택은 “자동 정리(self-cleaning)” 특성을 가지고 있어 성능이 좋습니다.
  • : 가비지 컬렉터(Garbage Collector, GC)가 관리하며, 스택보다 느리고 프로그램 전체에 영향을 줄 수 있는 지연(latency)을 유발할 수 있습니다.

변수가 스택에 할당되는지 힙에 할당되는지 결정

Go 컴파일러는 변수의 사용 방식에 따라 스택 또는 힙에 할당 여부를 결정합니다. 이 과정은 **탈출 분석(Escape Analysis)**을 통해 이루어집니다.

탈출 분석의 주요 원칙

  1. 변수가 함수 반환 후에도 참조될 가능성이 있다면, 해당 변수는 힙에 할당됩니다.
  2. 변수가 너무 커서 스택에 저장하기 어렵다면, 힙에 할당됩니다.
  3. 컴파일 시점에 변수의 크기를 알 수 없다면, 힙에 할당될 가능성이 높습니다.

공유 방향에 따른 메모리 위치

  • 공유 다운(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 함수 호출 시 nx는 각각 스택 프레임(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"
  • 이 명령어는 컴파일러의 최적화 결정을 출력하며, 어떤 변수가 힙으로 탈출했는지 보여줍니다.