관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 1. Swift 메모리 할당 - address 확인 방법, withUnsafePointer, Memory Graph Debugger, lldb의 memory read, stack과 heap의 메모리 주소 범위 확인 (vmmap) 본문

iOS 응용 (swift)

[iOS - swift] 1. Swift 메모리 할당 - address 확인 방법, withUnsafePointer, Memory Graph Debugger, lldb의 memory read, stack과 heap의 메모리 주소 범위 확인 (vmmap)

jake-kim 2023. 8. 11. 01:08

1. Swift 메모리 할당 - address 확인 방법, withUnsafePointer, Memory Graph Debugger, vmmap

2. Swift 메모리 할당 - 타입별 메모리 할당 위치 (stack, heap)

3. Swift 메모리 할당 - 타입별 메모리 할당 위치 분석 심화 (String타입, struct안에 class, class안에 struct)

4. Swift 메모리 할당 - MALLOC, MALLOC guard, dyld private memory, Shared memory, ColorSync (vmmap PID)

사전 지식) 메모리 구조

  • swift에서의 struct, enum과 같은 value type은 메모리 주소가 높은 stack영역에 할당
  • class와 같은 reference type은 stack보다 메모리 주소가 낮은 heap에 할당
  • static키워드를 사용한 프로퍼티는 static영역에 할당

메모리 구조 (RAM)

Swift 메모리 주소 확인 방법 1 - withUnsafePointer

  • Int(bitPattern:)에 메모리 주소를 넣으면 bit가 나오고 이 bit를 C스타일 포인터 주소 형식으로 변환하면 확인이 가능
// 코드 출처: https://gist.github.com/matsuda/0dbb9721a9ee8cddb8b09d885c8f4290
func address(_ o: UnsafeRawPointer) -> String {
    let bit = Int(bitPattern: o)
    return String(format: "%p", bit)
}

ex) strcut, heap 주소 출력해보기

var a = [1, 2, 3]
var classA = ClassA()
var structA = StructA()
var b = [1, 2, 3]
var vc = UIViewController()

address(&a)        // (value type) 0x600001ce51e0
address(&classA)   // (ref type)   0x16b5bf8e8
address(&b)        // (value type) 0x600001ce5220
address(&structA)  // (value type) 0x16b5bf8e0
address(&vc)       // (ref type)   0x16b5bf890
  • 주의할 점
    • swift에서 struct는 value type이라고 알았지만, memory 주소를 확인해보면 reference type인 classA, vc와 메모리 주소가 비슷하게 높음
    • swift내부적으로 struct 타입을 stack영역에 저장하는 것인가? -> 다음 포스팅 글에서 계속
  • 참조값이 아닌 heap에 할당된 형태를 볼때는 아래 코드를 사용하여 출력
    • 주의) class 타입을 아래처럼 출력하지 않으면 stack에 저장되는 reference 주소가 출력되므로 주의 (다음 포스팅 글 참고)
@inlinable func dump(object: AnyObject) {
    print(Unmanaged.passUnretained(object).toOpaque())
}

dump(object: vc) // 0x0000000158d41cd0
  • withUnsafePointer 바로 사용 
@inlinable func printMemoryAddress<T>(_ o: inout T) {
    withUnsafePointer(to: &o) { print($0) }
}

printMemoryAddress(&a) // 0x000000016dc278f0

Swift 메모리 주소 확인 방법 2 - Memory Graph Debugger

  • 실행한 상태에서 아래 아이콘 클릭

  • 메모리 참조 관계 파악이 용이

  • 오른쪽에 Address 정보도 파악이 가능

  • 왼쪽 7번째 탭을 봐도 메모리 주소 확인이 가능

cf) break point를 찍으면 활성화되는 thread별로 메모리 주소도 확인이 가능 (default 옵션)

lldb의 memory read

  • 실행중이고 위에서 확인된 메모리 주소를 인자값으로하여 LLDB에 입력하면 메모리에 관한 정보 획득이 가능 
    • 명령어: memory read <메모리 주소>
  • 예제를 위해 위에서 본 메모리 그래프 클릭

  • viewController의 메모리 주소 복사

  • memory read <address>를 사용하여 정보 출력
    • 8개의 비트가 모인 1바이트 씩 총 16byte로 두 줄이 출력
    • f7과 같은 각 쌍의 문자는 하나의 바이트를 의미
    • 이 바이트를 읽어서 구조와 같은 정보 추측이 가능

struct, heap 주소 범위 확인하기 (vmmap)

  • 앱을 실행하면 그 앱에 해당되는 메모리를 할당해주어야 하는데, 이 메모리의 정보를 알려주는 명령어가 vmmap (virtual memory map)
  • 예제를 위해 위에서 알아본 메모리 그래프 버튼 클릭

  • 왼쪽 7번째 탭에서 PID(Process ID) 확인 55295

  • terminal에 입력
    • 명령어: vmmap <PID>
    • 입력하면 여러가지 정보가 표출

  • stack 영역만 보려면 grep로 검색
    • vmmap 55295 | grep Stack
    • 이로써 stack 영역 확인이 가능
  • 메모리 주소는 9자리로 나오는데, 보통 16진수로 표현하므로 0x가 앞에 붙이고 앞에 0을 붙여주어야함 (실제로는 16진수로 16개의 숫자로 표현)
    • 0이 암묵적으로 7개가 생략되어 있어서, 직접 붙여주어야 메모리 전체 주소

16b40c000 -> 0x000000016b40c000

정리

  • 메모리 주소를 알아내는 방법은 다양하게 존재
  • 메모리의 주소를 알고, vmmap <PID> | grep stack을 통해 인스턴스가 어디에 할당되는지 분석이 가능
    • swift의 String, struct, struct안에 있는 class, class 안에 있는 struct의 메모리는 어디에 저장될까?(heap vs struct)
    • > 다음 포스팅 글에서 계속

* 전체 코드: https://github.com/JK0369/ExMemory

* 참고

https://gist.github.com/godrm/a2bff14ed36a98aa62f934a6ad426617#file-memorydump-swift

https://lldb.llvm.org/use/map.html

https://gist.github.com/matsuda/0dbb9721a9ee8cddb8b09d885c8f4290

https://courses.engr.illinois.edu/cs225/fa2022/resources/stack-heap/

Comments