[Swift] 메모리구조 / Memory Leak / ARC

륜재·2022년 9월 26일
0

Swift

목록 보기
1/2
post-thumbnail

1. Swift의 메모리구조

1-1. 메모리구조

프로그램이 실행될 때, RAM에 OS에 의해 할당받는 공간으로 코드(Code) 영역, 데이터(Data) 영역, 스택(Stack) 영역, 힙(Heap) 영역이 있다. 코드 영역은 실행할 프로그램의 코드가 저장되는 영역으로 텍스트 영역이라고도 한다. 컴파일 시 결정되고, 소스코드가 기계어 상태로 저장된다. 중간에 코드가 변경되면 안되기 때문에 Read-Only 형태로 저장된다. 데이터 영역은 프로그램의 전역 변수와 정적(static) 변수가 저장되는 영역으로, 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸한다. 실행 도중 변수 값이 변경될 수 있기 때문에 Read-Write로 저장된다.

1-2. Swift - Heap vs. Stack

1) Stack 영역은 함수 호출 시 지역변수와 매개변수, 리턴값 등이 할당되는 메모리 공간이다. 함수 호출이 완료되면 메모리 할당이 해제된다. 컴파일 시 데이터의 크기가 결정되고 ValueType의 경우 스택에 할당된다.

2) Heap 영역은 프로그래머가 할당/해제하는 메모리 공간으로 동적으로 메모리를 할당한다. Swift의 경우, ARC를 통해 더 이상 참조되지 않는 메모리는 자동으로 해제된다. 런타임 시 데이터의 크기가 결정된다. 일반적으로, 클래스(Class) 인스턴스와 같은 ReferenceType의 경우에 힙에 할당된다. 다만, 클래스 내부의 구조체(Struct) 인스턴스와 같은 경우 ValueType도 힙에 할당될 수 있다.

2. Memory Leak

Memory Leak (메모리 누수)란?

프로그램이 더 이상 필요하지 않은 메모리 영역을 계속 점유하고 있는 현상으로, 누적되면 메모리를 낭비하게 된다.

3. GC vs ARC

3-1. GC(Garbage Collection)이란?

JAVA 등 프로그래밍 언어에서 사용되는 메모리 관리 방식으로, JVM의 Heap영역에서 동적으로 할당했던 메모리 영역 중 필요하지 않게 된 메모리 영역을 주기적으로 삭제하는 프로세스이다.

3-2. ARC(Automatic Reference Counting)이란?

Swift에서 사용하는 메모리 관리 방식으로, 더 이상 필요하지 않게 된 클래스의 인스턴스를 메모리에서 해제하는 방식이다.

3-3. ARC 작동 규칙

1) 강한참조 (Strong Reference)

강한참조는 인스턴스를 다른 인스턴스의 프로퍼티나 변수, 상수 등에 할당할 때 참조 횟수가 1 증가한다. 강한참조를 사용하는 프로퍼티, 변수, 상수 등에 nil을 할당하면 참조 횟수가 1 감소한다.

2) 약한참조 (Weak Reference)

약한참조는 자신이 참조하는 인스턴스의 참조 횟수를 증가시키지 않는다.

3) 미소유참조 (Unowned Reference)

약한참조와 마찬가지로 인스턴스의 참조 횟수를 증가시키지 않는다. 그러나 미소유참조는 약한참조와 다르게 자신이 참조하는 인스턴스가 항상 메모리에 존재할 것이라는 전제를 기반으로 동작한다.

3-4. 강한참조 순환

강한참조 순환이란, 인스턴스끼리 서로가 서로를 강한참조할 때 각각 인스턴스에 nil을 할당하더라도 두 인스턴스 모두 참조횟수가 1이 남아 메모리에서 해제되지 못해 메모리 누수(Memory Leak)가 발생하는 상황이다.

정상적인 강한참조

강한참조 순환의 예시

3-5. 강한참조 순환 문제 해결 방법

1) 수동 해결

2) 약한참조 해결

약한참조는 자신이 참조하던 인스턴스가 메모리에서 해제된다면 nil이 할당될 수 있어야 하기 때문에 옵셔널에만 쓰일 수 있다.

3) 미소유참조 해결

미소유참조는 사람이 신용카드를 소지하지 않을 수는 있지만, 신용카드는 명의자가 꼭 있어야하는 경우처럼, 어떠한 인스턴스가 소유자가 반드시 존재해야만 존재할 수 있는 경우에 사용한다.

ARC와 GC의 차이

ARC와 GC의 가장 큰 차이점은 메모리 해제 시점을 정확히 알 수 있는지 여부이다.

우선, ARC는 컴파일과 동시에 참조를 계산하기 때문에, 메모리 해제하는 시점을 정확하게 알 수 있다. 컴파일 시 이미 인스턴스의 해제 시점이 결정되어 있기 때문에, 메모리 관리를 위한 시스템 자원을 추가할 필요가 없다는 장점이 있다.
반면, GC는 런타임 중 참조를 계산하여 메모리에서 해제하기 때문에 개발자 입장에서는 메모리 해제 시점을 정확하게 알 수 없다. 런타임 시 프로그램 동작 외에 메모리 참조 계산을 위해 추가 자원이 필요하기 대문에, 성능 저하 발생 가능성이 있다.

ARC와 GC의 다른 차이점은, 앞서 정리했던 Memory Leak 가능성 측면에서 볼 수 있다.

ARC는 개발자가 작동 규칙을 알아야 하고, 그렇지 않으면 강한 참조 순환과 같이 인스턴스가 메모리에서 영원히 해제되지 않을 가능성이 있다.
GC의 경우 JVM에 탑재되어 있는 가비지 컬렉터가 메모리 관리를 대신하기 때문에 규칙에 신경쓰지 않아도 되고, 상호 참조 상황 등의 복잡한 상황에서도 인스턴스를 해제할 가능성이 높다.

참고자료

  1. Swift Docs-ARC
    https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html
  2. 야곰, 스위프트 프로그래밍, 한빛미디어(2019), p.471-493
  3. https://lsh424.tistory.com/77
  4. https://shark-sea.kr/entry/iOS-Swift-메모리의-Stack과-Heap-영역-톺아보기?category=785515

1개의 댓글

comment-user-thumbnail
2022년 9월 26일

LGTM bb

답글 달기