프로그램이 실행되면 운영체제(OS)는 메모리(RAM)에 프로그램을 위한 공간을 할당한다.
그 공간은 총 4가지 Code
, Data
, Heap
, Stack
으로 나뉘어져 있다.
작성한 소스 코드가 기계어 형태(0,1로 구성)로 저장된다.
CPU가 여기에 저장된 명령어를 하나씩 가져가 처리한다.
중간에 코드가 변경되지 않도록 Read-Only 형태로 저장된다.
전역변수
, static변수
가 저장된다.
프로그램 시작과 동시에 할당되고 프로그램이 종료되어야 메모리가 해제된다.
실행 도중 값이 변경될 수 있어서 Read-Write 형태로 지정된다.
클래스 인스턴스
, 클로저
같은 참조 타입의 값이 힙에 할당된다.
Code, Data, Stack 중에 유일하게 런타임 시 크기가 결정된다.
사용하고 난 후에 반드시 메모리 해제를 해줘야하는데, ARC
를 통해 힙에 할당된 메모리가 더 이상 참조되지 않으면 자동으로 해제된다.
(📌 ARC는 더 자세히 알아보도록 하자!)
지역변수
, 함수 호출 시 함수의 매개변수
, 리턴 값
등이 저장되고 함수가 종료되면 저장된 메모리도 해제된다.
마지막에 생성된 변수가 가장 먼저 해제되는 LIFO
(last in, first out) 데이터 구조이고 CPU에 의해 관리되고 최적화되어서 속도가 빠르다.
// 전역변수, Data 영역에 저장됨
var a: Int = 10
var b: String = "hs"
struct data {
static let data = "data" // 타입변수, Data 영역에 저장됨
}
class Person {
var name: String?
}
let person = Person()
// person 변수는 Stack에 저장됨
// Person() 인스턴스는 Heap에 저장됨
func main(){
var person = Person()
}
// person 변수는 Stack에 저장됨
// Person() 인스턴스는 Heap에 저장됨
func add(a: Int, b: Int) -> Int {
let result = a + b
return result
}
// 매개변수 a, b, 지역변수 result 다 Stack에 저장됨
Stack은 메모리가 한정되어 있기 때문에 너무 큰 메모리는 할당할 수 없다.
데이터의 크기를 모르거나, Stack에 저장하기엔 큰 데이터의 경우엔 Heap에 할당 하고 그 외엔 Stack에 할당하면 된다. (인스턴스, 클로저 등 자동으로 힙에 할당되는 것 외에 직접 할당할 경우) 만약 Stack에 너무 많은 메모리를 할당하게 되면 Stack over flow가 발생한다.
💡 Stack over flow?
스택에 너무 많은 메모리를 할당하게 되어 자신의 스택 영역을 초과한 경우,
iOS에서 Stack over flow 발생 시 앱이 crash된다.
변수는 Stack에 저장되고, 그 변수는 Heap의 주소 값을 갖는다.
클래스 인스턴스를 Heap에 저장하려면 어디에 저장할지 찾아야 하고 멀티 스레드 환경에서 Locking, 해제과정 등 시간이 소요되기 때문에 클래스를 사용하는것은 Stack에 저장되는 구조체를 사용하는 것보다 오래 걸린다.
클래스와 구조체 두개 다 사용 가능한 경우, 구조체를 사용하는것을 권장하고 상속이나 참조와 같은 클래스만의 기능을 사용해야 할 경우 클래스를 이용한다.
힙과 스택은 같은 메모리 영역을 공유한다.
같은 메모리 공간에서 힙 영역은 낮은 메모리 주소부터 할당받고,
스택 영역은 높은 메모리 주소부터 할당 받는 것이다.
따라서 힙 또한 자신의 영역 외로 확장하려다 보면 Heap over flow가 발생한다.