ARC & Memory

DevMinion·2022년 7월 17일
2

ARC와 메모리를 알아보쟈🙌
메모리 관리는 매우 중요하다! 이는 비단 PC에서 뿐만 아닌 모바일 기기에서도 마찬가지이다. 오늘은 ARC를 통한 메모리 자동 해제와 메모리에 대해서 알아보자.

Memory

이전 포스트인 Class와 Struct 에서 짧막하게 메모리의 Stack영역과 Heap영역에 대해서 다루어 보았다. (한 번만 봐줘요...) 보다 Deep한 메모리 영역에 대해서 공부해보자.

Memory의 구조

메모리는 Code, Data, Stack, Heap이렇게 4가지 영역을 갖는다. 순서대로 보다 자세히 알아보자.

Code

Swift로 열심히 작성한 코드는 기계어로 변환되어 해당 Code영역에 저장된다.

Data

프로그램의 전역 변수, 정적 변수가 해당 Data영역에 저장된다. 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸(메모리 해제)된다.

Heap

앞선 Class와 Struct 포스트에서도 간략하게 다뤘지만, 해당 Heap영역은 개발자가 직접 관리할 수 있고, 관리를 해야만! 하는 영역이다. 우리는 뒤에서 언급할 ARC를 통해 보다 쉽게 Heap영역을 관리할 수 있고 해제할 수 있다.
Heap영역은 메모리의 낮은 주소에서 높은 주소로 할당된다. 또 런타임 단계에 결정되어 메모리 크기에 대한 제한이 없음!

Stack

앞선 Class와 Struct 포스트에서도 간략하게 다뤘지만, Stack영역은 우리가 자주 사용하는 Stack 자료구조를 사용하여 PUSH()와 POP()을 통해 자동으로 메모리 해제가 가능하다. 즉 메모리를 직접 해제해주지 않아도 된다는 말씀!
Stack영역은 메모리의 높은 주소에서 낮은 주소로 할당된다. 단 컴파일 단계에 결정되어 Heap과는 다르게 메모리 크기에 대한 제한이 있음!

ARC

ARC와 GC

Swift에 ARC가 있다면, 자바에는 GC(Garbage Collection)이 있다. ARC와 GC의 차이를 간단하게 알고 넘어가자.

GC란

자바등의 언어에서 사용하는 Garbage Collection이다. Garbage Collector라는 친구가 프로그램 실행(런타임 단계) 중에 동적으로 감시하고 있다가, Garbage라는 어떠한 변수도 가리키지 않는 더 이상 필요 없어진 메모리 영역을 찾아 메모리에서 삭제하는 것이다. 여기서 매우 중요한 것은 런타임에 메모리 관리를 행한다는 것이다.

ARC란

기존의 Object-C에서 메모리 관리를 하던 방식은 MRC(Manual Reference Counting)이다. 이는 개발자가 직접 Retain, Release 코드를 삽입하여 메모리를 수동으로 해제하는 방법이다. 이를 자동으로 컴파일 단계에서 컴파일러가 개발자를 대신해 자동(Automatic)으로 Release코드를 삽입하여 메모리 관리를 행하는 방식을 말한다.

ARC와 GC의 차이 정리

GC는 런타임에서 동작하기에 오버헤드가 존재한다. 항상 메모리에 상주하며 감시해야하기에 메모리 사용량이 늘어날 수 밖에 없으며, CPU 자원을 일부 사용해야한다. 하지만 ARC는 컴파일 단계에서 동작하기에 GC에서 존재하는 오버헤드 문제에서 비교적 자유롭다. 따라서 성능이 제한적인(CPU연산 및 메모리 공간 등) 모바일 기기에서는 보다 효과적이다. 너무 GC만 까고 ARC만 칭찬한 것 같아 추가하자면 GC가 RC에 비해 인스턴스가 해제될 확률이 높다는 장점이 있다. 이는 런타임에서 동작하기에 당연한 결과이다.

ARC의 사용

Apple의 공식문서에 존재하는 예제 코드를 따라해보며 ARC의 동작을 살펴보자.

class Human {
    var name: String?
    var age: Int?
    
    init(name: String?, age: Int?) {
        self.name = name
        self.age = age
        print("\(name!) is being initialized")
    }
    
    deinit {
        print("\(name!) is being deinitialized")
    }
}

위처럼 Human클래스가 있다. (복습하자면 Class는 Reference type으로 Heap영역에 저장되며 당연하게도 ARC의 관리를 받을 수 있다.)

var reference1: Human?
var reference2: Human?
var reference3: Human?

reference1 = Human(name: "Minion", age: 24)

위처럼 3개의 변수에 Human 인스턴스를 할당한 후 reference1은 초기화까지 진행했다. 이 코드를 실행하면,

이렇게 정상적으로 초기화가 진행되었다는 메시지가 콘솔에 찍히는 것을 확인할 수 있다. 이는 reference1변수가 Human클래스에 강한 참조를 가졌다는 것이다. 그럼 이제 남은 reference2, 3변수도 강함 참조를 갖도록 해보자.

reference2 = reference1
reference3 = reference1

굿, 맛있게 참조를 먹었다. 자 그럼 3개의 참조를 갖게 되었다. ARC는 referenceCount(참조 수)가 0이 되어야 메모리를 해제한다. 그럼 reference1, 2만 해제한다면?

reference1 = nil
reference2 = nil


아직 콘솔에 변화가 없다. 만약 메모리가 deinit()되었다면 "Minion is being deinitialized"라는 로그가 찍혀야 한다. 그럼 reference3까지 강한 참조를 끊어보자.

reference1 = nil
reference2 = nil
reference3 = nil

이제 실행한다면?

이렇게 정상적으로 메모리가 해제되는 것을 확인할 수 있다.

순환 참조(Reference Cycle)

자칫하면 순환 참조가 발생하여 ARC를 통한 메모리 해제를 받지 못해 메모리 누수가 발생할 수 있다.
만약 두 개의 객체가 서로에 대해 강한 참조(Strong Reference)를 갖는다면, 이를 강한 순환 참조(Strong retain Cycle)이라고 하며 각각의 객체에 강한 참조를 해제해도 서로에 대한 강한 참조가 남아있어 ARC를 통한 메모리 해제를 하지 못하고 메모리에 남아있는 메모리 누수를 발생시킨다.

순환 참조는 다음 포스팅👈에서 보다 자세히 다뤄보도록 하겠다!

References(Thx🤙)

[Swift]ARC뿌시기 - naljin'smedium
[Swift]Automatic Reference Counting 정리 - 민소네(🤍)
Swift ARC에 대해서(1) - Ellie Kim
Automatic Reference Counting - Apple

profile
iOS를 개발하는 미니언

0개의 댓글