Memory Leak

JSLee·2022년 3월 10일
0

Memory Leak

프로그램에서 데이터를 메모리에 정한후 이것이 쓸모 없어지는 시점에서 적절하게 제거되지 않는 것을
Memory Leak 이라고 합니다.
이러한 경우 App 의 성능저하의 문제 , 심하면 App 자체가 죽어버릴수 있는 문제점입니다.

하지만 Swift 는 ARC( Automatic Reference Counting ) 를 사용하여 App 의 Memory 사용량을 추적하고 관리 합니다.

ARC 의 기능으로는

  • 자동으로 메모리를 관리한다.
  • Instance 에 대한 Reference Count 를 관리 하고 .zero 가 되면 자동으로 메모리 해제시킨다.
  • run time 에 계속 실행되는게 아니라 compile time 에서 실행된다.( build 할때 )

하지만 ARC 사용시에는
Retain Cycle 에 유의 해야합니다.

ARC 를 좀더 알기 앞써

retain , release

이 두개를 알아야 합니다.

retain ?

retain count ( reference count ) 의 증가를 통해 현재 Scope 에서 객체가 유지되는것을 보장 하는것

release ?

retain count ( reference count ) 를 감소 시킵니다. retain 후에 필요 없을 때 release 한다.

여기서 나오는 reference counting 은 Class Instance 에서만 적용됩니다.


ARC

WWDC2011 에서 ARC 가 등장 한 이후
Swift 는 ARC를 통해 메모리를 관리 합니다.


Class 의 새로운 Instance 를 생성할때 마다 ARC 는 Instance 의 정보를 메모리에 할당합니다.

ARC는 해당 인스턴스에서 사용하는 메모리를 해제하여 메모리를 대신 다른 용도로 사용할 수 있도록 합니다. 이렇게 하면 클래스 인스턴스가 더 이상 필요하지 않을 때 메모리 공간을 차지하지 않습니다.

하지만 ARC 가 사용중인 Instance 를 해제할 경우 더 이상 해당 Instance 속성에 액세스하거나 해당 Instance 메서드를 호출할 수 없습니다. 실제로 Instance 액세스하려고 하면 앱이 충돌할 가능성이 큽니다.

그럼 어떻게 ARC 쉽게 메모리 관리를 할수 있는가?

ARC 는 compile time 에 자동으로 retain release 를 적정한 위치에 삽입하기 때문입니다.

retain 은 reference count 증가 , release 는 감소를 뜻한다고 설명 했던 대로 삽입을 통한 증감으로 관리하고 있었습니다.

Heap

Heap 은 class closure 등 ReferenceType Data 들이 있는 공간이고 개발자가 동적으로 할당하는 메모리 공간이기 때문에 관리가 필요합니다.

Heap 에 얼마나 참조되고 있는지 Counting 하고 그에 따라 메모리를 할당,제거 하면 됩니다.(수동)

이것을 자동으로 해주는 기능이 ARC 입니다.


Strong Reference Cycle

  • 상수 , 변수 에 Class Instance 가 할당되면 해당 Instance 에 대한 참조(Reference) 가 생깁니다.

  • Class 는 Reference Type 이므로 Heap에 정보가 저장 됩니다.

  • Stack 은 그 Heap의 메모리영역을 가리키는 Pointer가 저장됩니다.

  • Reference Count 는 Heap 에 저장 됩니다.


사진출처

위의 소스코드의 겨우 Reference Count 는 1 이 됩니다.

하지만 여기서 point1 을 point2 에게 할당할 경우

상수,변수 에 Class Instance 를 할당할 경우 해당 Instance 에 대한 참조가 생기기 때문에

Reference Count 는 2가 되게 됩니다.

해당 Instance 를 유지하고 그 Instance 의 참조가 유지되는 한 ARC 는 할당을 해제하지 않습니다.

이를 Strong Reference 라고 합니다.


두개의 Class 가 있습니다.

Person 과 Apartment Class 에는 apartment , tenant 라는 옵셔널 변수가 존재합니다.

두개의 해당 Instance를 만들고 각자의 옵셔널 변수에 서로를 대입하여 연결시켜주게되면


앞써 설명했듯이 Class 간의 상수,변수에 Instance 를 할당하면 해당 Instance에 대한 참조가 생깁니다.

그로인해 이렇게 서로를 강하게 참조하고 있는 모습을 볼수 있습니다.

각자의 옵셔널 변수 들을 nil 로 해도 서로의 deinit 은 호출되지 않습니다.

강한참조는 메모리의 RC(Reference Count) 를 release(감소) 하지 않기 때문이죠

그렇기에 RC 는 0이 되지 않아 ARC가 메모리 해제를 하지 않습니다.

이렇게 서로를 강하게 참조하여 메모리의 누수를 일으키는 문제점을 해결수 있게

사용하는 방법들이 있습니다.

weak

weak 는 약한 참조를 뜻합니다.

RC 를 증가(retain) 시키지 않습니다.

weak 약한참조를 하려는 Instance 는 항상 변수 ( var ) 로 선언되어야 합니다.
또한 항상 Optional 이기도 해야합니다.

약한참조는 무엇이 다를까?

전과 다르게 tenant 는 weak 로 선언되었습니다.

이렇게 아파트 4A호실 에는 존이라는 사람이 살게 되었습니다.
서로간에 연결,참조가 완료되었습니다.

여기까지 같은지만

이렇게 서로간의 강한 참조 였던 두개의 인스턴스는 weak 약한 참조로 인해 서로간의 참조가 약해졌습니다.

약한참조를 어디서 사용해야 할까

Apple 공식 문서에서는

다른 인스턴스의 생명주기가 더 짧은 경우 다른 인스턴스를 먼저 해제 할수 있는 경우 사용

이라고 이야기 합니다.

위의 예시로 보았을경우 입주자 Person 과 거주지 Apartment 가 있지만

상식적으로 Person 의 경우 거주지인 아파트가 없을수 없다고 볼수 있습니다.

하지만 거주지 아파트의 경우 tenant (입주자) 가 Optional 일수도 있다고 볼수 있기에

tenant 에게 약한참조를 사용하는 거라고 볼수 있습니다.

하지만 만약 john 이 nil 이라면??

입주자가 갑자기 살아져 버리는 경우에는

deinit 이 호출됩니다.


Person 클래스의 Reference 는 총 2개입니다.

john 과 Apartment 클래스 가 Person을 참조합니다.

때문에 RC 가 2 이여야 하지만 weak 를 통한 참조가 있기에 RC 는 1입니다.

여기서 john 이 nil 이 되면 john 과 Person 의 참조가 끊어지게 되고

그로인해 RC가 0 이 되게 되는 것입니다.

그렇기에 ARC는 RC가 0 이므로 메모리에서 Person을 해제 하게됩니다.

그렇기에 deinit이 호출될수 있는 것이죠.


여기서 weak 의 특성에 대해서 조금더 살펴보게 되면

tenant 은 Person의 Instance 가 할당되어 있습니다.

하지만 john 이 nil 로 할당 되게 되면

  • Person 인스턴스에 대한 강한 참조가 끊어짐
  • RC = 0
  • ARC 의 메모리 할당 해제

그렇기에 runtime error 방지를 하기 위해서 항상 옵셔널과 변수로 선언되어야 합니다.

profile
iOS/Android/FE/BE

0개의 댓글