다트(Dart)에서의 메모리 관리와 관련된 개념인 스택(Stack), 힙(Heap), 데이터 영역(Data Segment), 그리고 가비지 컬렉션(Garbage Collection)에 대해 살펴보겠습니다. 다트는 메모리 관리를 비교적 자동화해 개발자가 직접 신경 쓰지 않도록 설계되어 있습니다. 하지만 기본적인 메모리 구조와 가비지 컬렉션의 작동 방식을 정확히 이해한다면 더 효율적인 앱을 만들 수 있습니다.
기본적인 3개의 메모리의 영역을 간단히 정리하자면
1. 스택(Stack)
스택은 함수 호출 시 지역 변수와 함수 실행 컨텍스트가 저장되는 메모리 영역입니다. 다트에서도 함수가 호출될 때마다 스택에 메모리가 할당되고, 함수가 종료되면 자동으로 해제됩니다.
- 특징: LIFO(Last In First Out) 구조로 작동하며, 함수 실행 시 스택 프레임이 쌓이고, 함수가 끝나면 프레임이 제거됩니다.
- 다트에서의 역할: 다트도 전통적인 방식처럼 스택을 사용하여 함수 호출 및 지역 변수 메모리를 처리합니다. 하지만 다트 개발자는 스택 메모리를 직접 관리할 필요는 없습니다.
- 제한사항: 스택 크기는 제한적입니다. 대용량 데이터나 매우 깊은 재귀 호출에서는 스택 오버플로우(Stack Overflow)가 발생할 수 있습니다.
2. 힙(Heap)
힙은 동적 메모리 할당이 이루어지는 영역입니다. 객체나 컬렉션과 같은 데이터가 프로그램 실행 중에 필요할 때 힙에서 메모리를 할당받습니다. 다트에서는 모든 객체가 힙에 저장되며, 스택에 저장되지 않습니다.
- 특징: 힙은 동적 메모리를 관리하며, 객체가 언제 해제될지 알 수 없기 때문에 가비지 컬렉션이 필요합니다.
- 다트에서의 역할: 다트는 객체지향 언어로, 모든 객체는 힙에 저장됩니다. 특히, List, Map, 그리고 사용자 정의 객체 등 동적 데이터가 주로 힙에서 관리됩니다.
- 장점: 다트에서 힙은 대용량 데이터를 처리할 수 있습니다. 메모리가 자동으로 관리되므로, 개발자는 메모리 할당 및 해제에 대한 부담이 적습니다.
3. 데이터 영역(Data Segment)
데이터 영역은 프로그램 실행 중 고정된 메모리 공간에 상수나 초기화된 전역 변수를 저장하는 공간입니다. 이 영역은 일반적으로 프로그램이 시작될 때 할당되고, 종료될 때까지 메모리에 유지됩니다.
- 특징: 이 영역에는 초기화된 전역 변수, 상수, static 변수 등이 저장됩니다.
- 다트에서의 역할: 다트는 전역 변수와 상수를 거의 사용하지 않고, 대신 최상위 변수(top-level variables)와 클래스의 static 변수를 사용하는 방식을 권장합니다. 이러한 변수는 데이터 영역에 저장될 수 있지만, 일반적인 개발에서 이 영역에 대해 의식적으로 관리할 필요는 없습니다.
예시로 아래 코드처럼 색상을 전역적으로 관리할 때, abstract 선언으로 객체를 만들 수 없게 하여 힙 영역을 쓰지 않고, 오로지 static const 필드로 앱 컴파일 시 자동으로 data 영역에 들어가며 프로그램 실행 시에 한 번만 메모리에 할당됩니다. 이후 재사용 시 별도의 메모리 할당이나 해제가 필요하지 않습니다.
abstract class AppColor {
AppColor._();
// Static const은 오로지 immutable함
static const Color mainThemeColor = Color(0xffFED655);
static const Color mainThemeColorBorder = Color(0xffFFCA42);
static const Color secondThemeColor = Color(0xffF16A56);
static const Color appbar = Color(0xffFED655);
static const Color transparent = Color(0x00000000);
static const Color hintText = Color(0xFF5C5C5C);
}
4. 가비지 컬렉션(Garbage Collection)
가비지 컬렉션은 힙 메모리에서 더 이상 사용되지 않는 객체를 찾아 자동으로 해제하는 메커니즘입니다. 다트는 자동 메모리 관리를 제공하기 때문에, 개발자는 객체를 명시적으로 해제할 필요가 없습니다. 다트의 가비지 컬렉터가 힙에서 메모리를 주기적으로 정리해주므로 메모리 누수를 방지할 수 있습니다.
- 다트에서의 가비지 컬렉션:
- 다트는 Mark-and-Sweep 방식의 가비지 컬렉션을 사용합니다. 이 방식은 사용하지 않는 객체를 찾고, 이 객체가 참조되지 않으면 메모리를 해제합니다.
- 힙에 있는 객체가 더 이상 참조되지 않으면, 가비지 컬렉터에 의해 자동으로 해제됩니다.
- Flutter 환경에서는 위젯 트리에서 불필요하게 남아 있는 객체나 UI 요소들도 가비지 컬렉터에 의해 정리됩니다.