# 개요

# iOS 메모리 WWDC 세션

memory footprint -> 프로그램이 실행되는 동안 사용하거나 참조하는 메인 메모리의 양

# 페이지

-> 페이지 당 사이즈는 일반적으로 16KB
-> 페이지 타입은 Clean & Dirty로 나눠짐
-> 앱 메모리 사용량은 페이지 수 X 페이지 크기

1KB 1000bytes -> int형 20000개면 80000bytes, 80KB니까 페이지는 6개

-> 50KB 이미지를 캐싱할 때 4페이지로 쪼개져서 메모리 매핑 진행됨

# 가상 메모리와 페이징에 대한 개념

iOS 메모리 뜯어보기 (opens new window)

  1. 프로그램 동작을 위한 메모리 영역은 운영체제에서 할당해준다.
  2. 물리적인 RAM 주소를 알려주는 것이 아닌 가상의 메모리 주소를 알려줌
  3. 가상 메모리 공간은 연속되지 않고 여러 군데 나뉘어 존재한다
  4. 페이징 방식이란 가상 메모리 공간을 일정 크기로 분할하여 각 공간을 물리적 메모리 공간에 매핑하는 방식을 말한다
  5. 가상 메모리 블록을 페이지라고 하고, 같은 크기의 메모리 블록을 프레임이라고 한다.
  6. 각 프로세스에는 페이지가 어떤 물리 주소에 매핑되어야 하는지의 정보를 저장하고 있는 페이지 테이블이 존재. 페이지 테이블에는 페이지 테이블 엔트리(PTE)들이 존재하며 각 PTE에는 프레임 번호와 함께 추가적인 상태 비트 저장

# 앱 사용량 (footprint)의 구성요소

-> Dirty

  1. 앱에서 사용된 메모리
  2. Array, NSCache, malloc, UIViews..
  3. 힙 할당, 객체들
  4. 디코딩 이미지 버퍼
  5. UIKit, CoreGraphics.. 프레임워크들

-> Compressed

  1. 액세스 되지 않은 페이지를 가져와서 압축 후 더 많은 공간을 만들어주는 공간
  2. iOS에서는 디스크 스왑을 제공하지 않는다. (하드 일부를 RAM처럼 사용하게 해주는 것)
    1. 메모리가 부족할때 당장 필요치 않은 데이터를 하드에 옮겨 사용
  3. 여러 페이지에 할당된 데이터가 있을때 액세스 빈도가 낮은 경우 자동으로 압축

-> Clean 구성 데이터

  1. 페이징 가능한 대상들
  2. memory mapped files (jpg 기타 등등)
  3. 프레임워크

# 메모리 경고?

didReceiveMemoryWarning()
-> 메모리 부족 현상이 발생한 경우 시스템에서 자동으로 호출되는 함수
-> 오버라이딩 하여 해당 함수 호출시 캐싱 정책을 디테일하게 만질 필요가 있을 수도 (캐싱 일시중단 or 백그라운드 작업제한)

override func didReceiveMemoryWarning() {
	cache.removeAllObjects()
	super.didReceiveMemoryWarning()
}

# 캐싱 주의점

  1. CPU 절약을 위해 캐싱을 사용하되, 너무 많이 사용하면 메모리를 모두 소진하여 문제 발생
  2. 컴프레서를 통해 액세스가 덜한 메모리를 잘 처리하기

# Memory Limit

  1. 기기마다 너무 다르다 (1GB ~ 4GB)

# 프로파일링

  1. VM Tracker
    • Xcode Instruments -> Dirty & Compressed 메모리를 트래킹할 때 사용
  2. VM Memory trace
    • 앱 가상메모리 시스템 성능 체크
    • 캐시 히트 체크
  3. 기기 메모리 제한에 접근 -> EXC 리소스 예외 발생, 디버거 자동으로 시작
    1. memgraph 보여줌
    2. CLI 환경에서 .memgraph 파일 내보낸 뒤 vmmap App.memgraph로 읽으면 메모리 소비에 대한 분석을 요약해줌
    3. 더티 메모리, 스왑 & 컴프레싱 메모리 양에 대해 세부 정보 제공
    4. 더티와 스왑이 중요한데, 스왑 크기가 압축 이후 데이터가 아니라 압축 이전의 크기를 제공한다는 것

# Images in iOS

-> 2048 X 1536 픽셀의 이미지가 있을때, 파일 사이즈는 590KB이지만, 메모리에 로드될 때는 10MB이다. (픽셀당 4바이트)
-> 실제 파일사이즈와 메모리상에서 사용되는 이미지 객체 크기 차이가 크기 때문에.. iOS에서 이미지가 다뤄지는 과정을 알아야함

이미지는 세 단계에 거쳐 렌더링된다.

  1. Load -> 원본 파일데이터 로드 (590KB)
  2. Decode -> GPU가 읽을 수 있는 형태로 변환 (10MB, 압축 풀기), SRGB 포맷
    • iOS에서는 풍부한 색 표현을 위해 와이드 포맷이라는 것을 사용함 (사용 안해도 됨)
    • 더 작게 알파 8포맷 혹은 루미넌스 포맷 사용 가능 (일반적으로는 사용 X)
    • 1바이트 ~ 8바이트까지 처리 가능하다는 것을 아는게 중요하다.
    • 어떤 형식을 선택하느냐? UIGraphics ImageRenderer API를 사용하면 가장 적합한 그래픽 방식을 선택함.
      • UIBezierPath 로 원을 그릴때 UIGraphicsBeginImageContextWithOptions 레거시 API를 사용하면 픽셀당 4바이트로 처리
      • UIGraphicsImageRenderer 로 처리한 경우 자동으로 픽셀당 1바이트로 처리
      • 동일한 마스크 사용하면서 tintColor만 사용하면 불필요한 메모리 사용량 줄일 수 있음
  3. Render -> 렌더링

이미지 리사이징

  1. UIImage는 리사이징 하는데에 비용이 많이든다.
    • 내부 좌표 공간 변화로 인해 성능이 조금 떨어진다.
    • 메모리 압축을 디코딩 과정도 포함되어서 성능에 추가비용 존재
  2. ImageIO API
    • 이미지 다운샘플링, 이미지
    • 로우레벨 -> 파일 앱에서 이미지 가져올때 사용해보기

백그라운드
-> 노티피케이션 센터의 UIApplicationDidEnterBackground 사용, 앱 백그라운드 진입시 이미지 언로드
-> 노티피케이션 센터의 UIApplicationWillEnterForeground 사용, 앱 포어그라운드 진입시 이미지 로드