본문 바로가기
Dev/WWDC 정리

[WWDC18] iOS memory deep dive - 1

by Mintta 2023. 3. 27.

메모리 사용량을 왜 줄일까

  • 더 나은 사용자 경험
  • 더 빠른 앱 실행
  • 시스템 성능 향상
  • 앱이 메모리에 더 오래 유지된다 (이건 뭘까)
  • etc

Memory pages

  • 힙에 여러개의 객체들을 담을 수 있다
  • Page Type
    • Clean: 값이 아무것도 안쓰여있는 상태
    • Dirty: 값이 하나라도 쓰여있는 상태
  • pages 수 * page 사이즈 = 앱의 메모리 사용량(memory in use)

Memory mapped files

  • read-only 파일은 항상 clean pages

Clean

  • 페이징할 수 있는 데이터
  • memory-mapped file
  • 이미지, data Blob, 학습 모델, 프레임워크
  • 모든 프레임워크에는 DATA CONST섹션이 있다.
  • method swizzling(????)같은 런타임 장난을 치면 실제로 더렵혀질(dirty) 수 있다.

Dirty

  • 앱이 쓴 모든 메모리를 말한다.
  • 할당된 모든 것: Objects, Strings, Arrays, etc..
  • Decoded image buffers, Framework
  • 프레임워크는 data section과 data dirty section이 있다.

앞서서 프레임워크에 관한 이야기를 두번이나 했다.

프레임워크는 memory와 dirty memory를 사용한다.

싱글톤과 global initializer(글로벌 생성자?)는 그들이 사용하는 dirty memory의 양을 줄이는 좋은 방법이다.

Compressed

Memory compressor

  • memory compressor가 액세스하지 않은 페이지를 가져와 압축하여 실제로 더 많은 공간을 확보할 수 있다.
  • 액세스하면 압축기가 압축을 해제하여 메모리를 읽을 수 있도록 한다.

  • 캐싱을 위한 Dictionary(3 pages)가 있는데, 한동안 해당 딕셔너리에 액세스하지않고, 시스템이 공간이 필요로한다면??

  • 위와 같이 3 pages를 1 page로 압축시켜서 공간을 만든다.
  • 그리고 만약 다시 딕셔너리에 접근한다면 메모리 공간이 다시 늘어날 것이다 (grow back).

Memory warnings

앱이 항상 메모리 경고의 원인은 아니다 !

작은 메모리의 기기에서 전화를 받다가, 그것이 메모리 경고를 일으킬 수도 있다. 그러니 메모리 경고가 당신의 원인이라고 가정하지 말자 !

그래서 이 compressor는 압축된 내용에 따라 실제로 이전보다 더 많은 메모리를 사용할 수 있기 때문에 메모리를 해제하는 것을 복잡하게 한다.

따라서 대신에 정책 변경을 권장한다. 예를 들어, 메모리 경고가 발생할 때 약간의 백그라운드 작업을 제한하거나 약간의 캐싱을 하지 않게끔 하는 것이다.

캐싱할 때는 반복적인 작업에서 CPU를 절약하려고 노력하지만, 캐시를 너무 많이 사용하면 메모리를 모두 사용하게 되므로 시스템에 문제가 발생할 수 있다

memory compressor와 cache가 있다는 것을 기억하고, 무엇을 캐시하고 무엇을 재계산할지 균형을 잘 맞추어야 한다.

한가지 더 참고할 점은 Dictionary 대신 NSCache를 사용하는 경우 캐시된 객체를 저장하는데 위협으로부터 안전한 방법이다.

NSCache가 메모리를 할당하는 방식 때문에, (사실 제거 가능하고) 메모리가 제한된 환경에서 훨씬 더 잘 작동한다

 

  • 우리가 흔히 앱의 메모리 사용량을 말하면 사실 Dirty와 Compressed segments에 대해서 말하는 것이다.
  • 모든 앱은 메모리 한계가 있고, 기기에 따라 이 한계는 다르다
  • Extensions은 더 작은 사용량 한계를 가진다.
  • 사용량을 넘기면 exception을 받는다
    • EXC RESOURCE EXCEPTION

메모리 사용량을 어떻게 프로파일 할 수 있을까

  • Xcode로 실행시켰을 때의 memory 탭
    • VM Tracker
      • VM Tracker는 Dirty, Compressed memory를 프로파일하기 좋은 방법
      • iOS에서는 압축 메모리인 dirty 트랙과 swapped 트랙이 따로 있으며, resident size에 대한 몇 가지 정보를 알려준다
      • 앱의 Dirty 메모리 크기를 조사하는데 매우 유용하다.
    • VM Memory Trace
      • 이것은 당신의 앱에 관한 가상 메모리 시스템의 성능에 대한 깊은 견해를 제공한다.Instruments

Instruments

앱을 실행시키다가 사용량 한계에 다다르면 Xcode에서 EXC resource exception을 보낸다. 그럼 이 때부터 memory debugger를 키고 조사를 시작하면 된다.

 

Xcode는 Memgraph 파일 포맷으로 당신의 앱에 대한 메모리 사용에 대한 정보를 저장한다. 우리는 Memgraphs를 command line tool로 사용할 수 있다 !

  1. Xcode에서 Memgraph를 export한다.
  2. Memgrap를 command line tool로 보낸다

vmmap

이와 같이 기존의 command line tool과도 아주 잘 쓸 수 있음

  • Dirty memory 사이즈가 늘어나는 것을 확인할 수 있다
    • -pages : page flag로 byte대신 page로 나타냄
    • grep ‘.dylib’ 어떤 프레임워크나 라이브러리가 dirty data에 기여하는지 찾기 위해
    • awk script: dirty col을 합산해서 마지막에 print한다

leaks

  • macOS 개발자들에게는 익숙할 수 있는 leaks라는 command line tool
  • 런타임에 아무데도 rooting되지 않은 힙의 객체를 추적한다.
  • leak이 있는 객체는 절대 해제할 수 없는 Dirty 메모리라는 점을 기억하자.

heap

  • 여기에는 Xcode에서 메모리 리소스 예외가 발생했을 때 찍은 Memgraph가 있고, 힙을 조사하고 싶다. 그래서 힙에 전달했고, 힙은 각 객체의 클래스 이름, 객체 수, 평균 크기 및 해당 객체 클래스의 총 크기에 대한 정보를 제공한다.
  • heap은 default로 count에 대해서 내림차순으로 정렬한다
  • 하지만 만약 가장 많은 것이 아닌 가장 크기가 큰 객체순으로 보고싶다면??

heap —sortBySize

  • heap —sortBySize 를 활용할 수 있다
  • 크기 가장 커다란 객체(NSConcreteData)를 확인할 수 있다

이것들이 어디서 왔는지 알아내야한다

  1. NSConcreteData 객체들 중 하나의 주소를 찾아야한다
  2. -addresses flag

  • 힙에 있는 각각의 인스턴스 주소를 줄 것이다
  • 이 주소들을 가지고 얘네들이 어디에서부터 왔는지 찾아낼 수 있다

 

3. 이 기능을 활성화하면 시스템에서 각 할당에 대한 back trace가 기록된다.
이러한 로그는 Memgraph를 기록할 때 capture되며, 일부 도구의 기존 출력에 주석을 달 때 사용됩니다.
Scheme Editor → Diagnostics. 

Scheme Editor → Diagnostics.

4. 이제 Memgraph가 malloc stack logging에 capture되면 이제 할당을 back trace해보자 이 때 도움 되는게 malloc history.

5. malloc_history [memgraph] [아까 위에서 찾은 객체 주소]

  • 해당 인스턴스에 대해 캡쳐된 backtrace가 있는 경우 이를 제공한다

6. NSConcreteData를 넣고 backtrace를 얻었다. 보아하니 NoirFilter의 apply 메서드가 커다란 NSData를 만들고 있었다

Summary

  1. 객체 생성을 보고 싶은가?
  2. 어떤게 메모리에서 객체나 주소를 참조하는지 보고 싶은가?
  3. 인스턴스 크기가 얼마나 큰지 확인하고 싶은가?

→ malloc stack logging이 활성화된 경우, 프로세스가 시작될 때 malloc기록을 통해 해당 객체에 대한 backtrace를 찾을 수 있다

메모리에 있는 객체를 어떤게 참조하는지 보고 싶다?

→ leaks 와 그 옵션들을 활용하면 된다

region이나 인스턴스가 얼마나 큰지 확인하려는 경우

→ vmmap 과 heap이 제격이다


해당 세션을 듣고 메모리 디버깅을 통해서 성능 개선을 해본 적용기

[iOS] 메모리 힙할당 줄이기 및 누수 해결하기

 

댓글