소개
앞선 python의 closure 에 대한 글을 작성하다가 문뜩 이런 생각이 들게 되었습니다.
- Closure에서 nonlocal 변수들은 메모리에서 어떻게 저장되고 처리가 되는거지?
- scop(외부함수)에 묶여 있는 변수(nonlocal)들은 nested function을 return 하고 사라지는데 어떻게 남아있게 되는거지?
이런 질문이 들어 closure 의 메모리 처리 방법에 대해서 찾아보고, 관련 내용을 정리해보려 합니다.
먼저, C/Assembly 관점에서 프로세스가 실행될 때의 메모리 구조는 아래와 같습니다.
Machine Languge / C / Assembly 관점에서 메모리
영역 | 역할 |
---|---|
Code | 기계어가 쌓이는 영역이다. 쌓인 기계어를 CPU가 수행해 프로그램 작동된다. |
Data | 전역변수, 정적변수, 상수가 저장된다. 내부적으로 변수와 상수(write 막기위해)는 분리해 저장한다. |
Stack | 함수 호출시 사용한다. 함수의 매개변수, 지역변수, 반환 주소값 저장한다. 크기가 작다. |
Heap | 동적 사용할 수 있다. malloc() / free() 를 사용하여 할당 및 해제 가능하다. 주로 동적으로 메모리 잡고 포인터로 참조된다. |
Closure 메모리 사용
closure는 함수로 구현이 되는 만큼, 함수가 call 되고 return 될시 사용되어지는 stack 의 컨셉을 이용합니다.
Stack frame 은 아래와 같은 특징을 가집니다.
- 함수가 call 될 때 분리된 scop 으로 사용 됨.
- LIFO(Last In First Out) 방식으로 마지막으로 들어간 데이터가 먼저 나오는 형식으로 데이터를 관리함.
- 만약 함수가 return 되면 사용하였던 stack frame 은 pop 되어 다음 함수가 사용할 수 있게 비워줌.
그런데, C/Assembly 이외 대부분의 higher level 프로그래밍 언어 관점에서는 stack을 flat array 대신에 linked list 또는 hash table 로 구현이 되어 있습니다. 이러한 방법 때문에 stack을 runtime에 재정렬을 할 수 있게 하고, 물리적 메모리 레이아웃에 제한을 받지 않도록 해줍니다. 그리고 stack을 linked list 또는 hash table 로 구현을 했다는 것은 결국 c/assembly 관점에서의 heap을 이용하여 구현이 되었다고 볼 수 있습니다.
즉, higher level language 에서의 stack은 실제 C나 Assembly level 에서의 heap 을 이용하여 linked list 또는 hash table로 구현이 된다고 볼 수 있습니다.
python에서 아래와 같은 closure 사용한 코드의 메모리 구조를 본다면 아래와 같습니다.
def outer_func(x):
num = 1
def inner_func():
nonlocal num
num = num + x
print(num)
return inner_func
myfunc = outer_func(10)
myfunc()
참고
- stackoverflow.com/questions/29225834/where-are-variables-in-a-closure-stored-stack-or-heap
- stackoverflow.com/questions/26061856/javascript-cant-access-private-properties/26063201#26063201
- stackoverflow.com/questions/556714/how-does-the-stack-work-in-assembly-language