티스토리 뷰

RxSwift

[RxSwift] Closure ARC / Memory Leak 실험

Hani_Levenshtein 2021. 7. 10. 04:18

안녕하세요 Hani입니다.

이번엔 RxSwift 클로저 강한 순환 참조 및 메모리 누수에 대하여 알아보겠습니다.

 

 

실험 방법은..!

버튼을 누르면 해당되는 뷰 컨트롤러가 push되고 Observable이 1만 번 생성됩니다.

그리고 뷰컨을 다시 pop해서 돌아올 거예요.

 

이때 메모리 누수가 생기는 지 메모리 변화를 보면서 알아보겠습니다.

 

그럼 시작 🔥


1번 실험 

Observable이 종료(onCompleted/onError)되지도 않았고,

subscribe이후에 Disposable이 DisposeBag에 들어가지도 않았습니다.

 

 

3번 push/pop을 반복한 모습입니다.

push했을 때 메모리 사용량이 증가했지만 pop했을 때 로그에 deinit이 찍히지 않았고 메모리 사용량도 감소하지 않았습니다.

 

요런 모습 😡


2번 실험 Weak Self

Observable이 terminated되지도 않았고,

Disposable이 DisposeBag에 들어가지도 않았습니다.

단, 이번에는 약한 참조를 하고 있습니다.

 

 

push했을 때 메모리 사용량이 증가했고 pop했을 때 로그에 deinit이 찍혔습니다.

그런데 메모리 사용량은 줄어들지 않았네요 🥲

 

 

뷰컨이 deinit이 된다고 해서 종료되지 않은 Observable도 같이 사라지지 않는다..!

뷰컨이 deinit되어도 Observer가 Observable을 subscribe하고 있는 게 유지된다면

Disposable이 살아있는 상태기 때문,,

 

 

 

만약 disposable들을 dispose해주면 어떨까 🧐


3번 실험 Weak Self + DisposeBag

오우..!

 

일단은 pop했을 때 강한 순환 참조가 일어나지 않으니까 뷰컨의 deinit이 불립니다. 

 

뷰컨이 정상적으로 deinit되면 뷰컨의 멤버 변수 disposeBag이 사라지고..

DisposeBag인스턴스를 참조하고 있던 disposeBag변수가 사라졌으니

DisposeBag인스턴스의 Reference Count가 0이 됩니다.

 

그럼 DisposeBag인스턴스가 메모리에서 사라지겠죠?

DisposeBag의 deinit은 위와 같이 정의되어있는데..!

deinit속 dispose는 아래와 같이 정의되어 있습니다.

 

DisposeBag에 담겨있던 모든 disposable들을 dispose시킵니다..!

 

 

 


4번 실험 Weak Self + onCompleted

 

 

subscribe에서 onCompleted이나 onError를 만나면 클로저가 사라지고 (바로 사라져서 메모리가 22메가까지만 올라가나?)

pop했을 경우 뷰컨이 정상적으로 deinit되면서 메모리가 사라져서 누수가 없다..!

 

흠.. 메모리가 하늘로 솟지는 않는데  =_=

왜 뾰족한 곳이 균일하게 만들어지지 않지

갑갑하구먼

 


5번 실험 DisposeBag

흑흑.. 

 

뷰컨을 pop했을 때 뷰컨의 Reference Count가 0으로 내려가지 않습니다.

DisposeBag에 disposable이 담겨있기는 하지만 뷰컨의 deinit이 불리지 않기 때문에 결국 DisposeBag도 사라지지 않는..

 

결론은 ARC.


6번 실험 onNext-onCompleted

 

onCompleted에 의해 클로저가 바로 메모리에서 사라지니까 

 

이런 모양이 됩니다.

뷰컨의 Reference Count도 하나 줄었고..!

pop을 하면 Reference Count가 0이 되니까 정상적으로 deinit되는 모습입니다.

 


7번 실험 onNext-onCompleted + DisposeBag

위와 동일하지만 Disposable을 disposeBag에 담는 차이가 있습니다.

근데 completed되면서 클로저가 사라지니까 결과는 동일!

 

얘도 정상적으로 deinit되고 메모리도 안 터지긴 하는데..

왜 DisposeBag만 쓰면 메모리가 위로 솟지 🥲

 


암튼 실험 결과는

1. 아무것도 안 해줌 -> ARC 발생

2. weak self -> 뷰컨 deinit은 됐지만 subscribe을 통한 클로저가 안 사라짐

3. weak self + disposeBag -> 굳

4. weak self + onCompleted -> 굳

5. disposeBag -> ARC 발생

6. onCompleted -> 굳

7. onCompleted + disposeBag -> 굳

 

결론은

1. 일단은 disposeBag으로 꽁다리를 막아놓자.

2. terminated 되지 않는 Observable에 의해 클로저가 쭉 캡쳐리스트를 들고 있으니 조심하자.

3. weak self도 써놓으면 손해는 안 볼 것 같은데..!

4. 메모리 함정이 너무 많다. 😅


References

https://github.com/Hani-Levenshtein/RxSwift/tree/main/disposeBag

댓글