힙 영역은 개발자가 직접 공간을 할당하고, 해제하는 영역이고, 클래스 인스턴스나 클로저와 같은 참조 타입은 자동으로 힙 영역에 할당된다고 했습니다. 그리고 공간을 할당했으면, 사용이 끝난 시점에 메모리 해제를 꼭 해줘야 한다고 했었죠? 그렇지 않으면 메모리 누수가 발생할테니까요 :)
이 부분이 이해가 되지 않는 다면 이 글을 참고해주세요
그런데 우리는 Swift로 개발을 하면서 클래스 인스턴스나 클로저의 메모리 해제를 해주는 작업을 따로 한적이 없습니다. 그렇다면, 지금까지 계속 메모리 누수가 발생하는 코드를 작성했던 것일까요?
iOS에서는 힙 영역에 할당된 메모리를 자동으로 관리해주는 ARC(Automatic Reference Conting)라는 녀석이 있어서 굳이 release, free와 같이 메모리를 직접 해제해주지 않아도 쓸모가 없어지는 시점에 자동으로 메모리 해제를 해줍니다!
ARC를 본격적으로 공부하기에 앞서 먼저 참조 타입과 힙 영역에 대해서 공부하도록 하겠습니다.
힙 영역에는 클래스 인스턴스나 클로저와 같은 참조 타입을 자동으로 할당한다.
ARC에서 관리하는 메모리 영역이 바로 힙 영역입니다. 그래서 실제 참조 타입을 사용할 때 메모리가 어떤 식으로 할당되는지 알아보도록 하겠습니다.
class Animal {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let syonge = Animal(name: "숑이", age: 8)
Animal 이라는 클래스가 있고, 예제를 간단하게 진행하기 위해서 syonge는 전역변수지만, 보통 개발할 때와 마찬가지로 어느 클래스에서 생성한 지역변수라고 생각해주세요.
그럼 메모리에는 다음과 같이 할당됩니다.
syonge는 지역변수이기 때문에 스택 영역에 저장될 것이고, Animal 인스턴스는 참조 타입이기 때문에 힙 영역에 저장됩니다.
이 때, syonge는 힙 영역에 생성된 인스턴스의 주소값을 가지고 있습니다.
let clone = syonge
참조 타입이기 때문에 위 코드는 인스턴스를 복사하는 것이 아니라 syonge가 참조하고 있는 인스턴스를 clone도 참조하게 되겠죠?
이렇게 힙 영역에 Animal 인스턴스를 하나만 할당하고, 2개의 지역변수가 참조하고 있겠죠
근데 힙 영역에서 중요한 것은
더 이상 메모리에 있을 필요가 없는(사용하지 않는) 메모리 공간을 해제해줘야 한다는 것
근데 우리는 직접 메모리를 해제해주는 작업을 해준적이 없습니다. 그래도 메모리 누수가 발생하지 않았고, 지금까지 아무런 문제없이 참조 타입을 원하는 만큼 할당해서 사용했습니다.
이게 가능했던 이유는!!!!
iOS에서 ARC(Automatic Reference Counting)가 더 이상 사용하지 않는(참조되지 않는) 힙 영역의 인스턴스 메모리를 자동으로 해제해주기 때문입니다.
참조 타입을 생성할 때마다 힙 영역에 메모리가 할당되고, 할당된 인스턴스가 필요 없어지면, ARC가 알아서 메모리를 해제해준다.... 라고 했죠? 그렇다면 메모리 해제를 해야할 시점을 ARC가 어떻게 알고, 자동으로 메모리를 관리해주는 것 일까요? 이 물음에 대한 답은 ARC 이름에서 얻을 수 있습니다.
사실 클래스 인스턴스나 클로저 같은 참조 타입을 선언해서 힙 영역에 저장될 때, Reference Count(참조 횟수) 라는 것이 같이 저장됩니다.
let syonge = Animal(name: "숑이", age: 8)
syonge라는 지역변수가 힙 영역에 할당된 Animal 인스턴스를 참조하고 있습니다. 따라서 RC 값은 1입니다.
let clone = syonge
clone이라는 지역변수도 Animal 인스턴스를 참조하게 됐습니다. 따라서 RC 값은 2입니다.
syonge가 더이상 Animal 인스턴스를 참조하지 않거나, 메모리에서 해제되는 경우 Animal 인스턴스의 RC 값은 1 감소하게 됩니다.
마찬가지로 clone도 Animal 인스턴스를 참조하지 않으면 해당 인스턴스의 RC값이 1 감소했고, 결국 RC값이 0이 되었습니다.
이렇게 Reference Count는 힙 영역에 할당된 참조 타입 인스턴스가 참조되는 횟수를 카운트하고, Reference Count가 0이되면(참조 되지 않음) ARC에서 자동으로 메모리를 해제합니다.
사실 ARC라는 놈이 자동으로 메모리를 관리를 해주기는 하지만, 개발자의 실수로 순환 참조가 발생하면, Reference Count가 0이 되지 않아서 앱이 종료되기 전까지 메모리에서 해제되지 않는 상황이 발생한다.
즉, 아무리 ARC가 자동으로 메모리를 관리해준다 해도 제대로 활용할줄 모르면, memory leak이 발생할 수 있다.
순환 참조에 대해서는 다음에 올리도록 하겠습니다.