Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 스위프트
- Observable
- swift documentation
- uitableview
- SWIFT
- Clean Code
- Human interface guide
- UICollectionView
- combine
- ribs
- tableView
- Refactoring
- 리펙터링
- MVVM
- 리팩토링
- clean architecture
- uiscrollview
- Protocol
- map
- 애니메이션
- RxCocoa
- 클린 코드
- rxswift
- swiftUI
- 리펙토링
- UITextView
- ios
- collectionview
- HIG
- Xcode
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 3. Swift 메모리 할당 - 타입별 메모리 할당 위치 분석 심화 (String타입, struct안에 class, class안에 struct) 본문
iOS 응용 (swift)
[iOS - swift] 3. Swift 메모리 할당 - 타입별 메모리 할당 위치 분석 심화 (String타입, struct안에 class, class안에 struct)
jake-kim 2023. 8. 13. 01:531. 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)
메모리 어디에 할당되는지 확인 방법 복습
- 1번 글, 2번 글에서 살펴본대로 아래 함수로 각 프로퍼티가 어떤 메모리에 할당되는지 확인이 가능
@inlinable func printMemoryAddress<T>(_ o: inout T) {
withUnsafePointer(to: &o) { print($0) }
}
- vmmap(virtual memory) 명령어를 사용하여 stack 메모리 파악
- vmmap <PID> | grep Stack를 사용하여 각 스레드 별 stack의 메모리 범위 파악이 가능
- 위에서 얻어온 메모리 위치가 이 stack 메모리 범위안에 속한다면 stack 영역에 할당되었다고 판단
- (자세한건 이전 포스팅 글 에서 Int 타입 프로퍼티의 메모리 할당 위치 파악 참고)
String 타입의 메모리 할당 위치
- string 프로퍼티 메모리 확인
var stringValue = "a"
printMemoryAddress(&stringValue) // 0x000000016b8038c8
- 현재 실행중인 PID 확인
- stack 메모리 범위 확인
vmmap 61277 | grep Stack
확대해서 보면, 5개의 Stack 메모리 공간이 존재
- stringValue 프로퍼티의 주소는 16b8038c8이므로 thread 0에 할당됨을 확인가능
String타입인 copy by value 메모리 주소
- 이전 예제보다 긴 문자열 하나와 copy by value한 프로퍼티 준비
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var stringValue = "iOS 앱 개발 알아가기 - string은 과연 stack에 저장될까? heap에 저장될까?"
var stringValue2 = stringValue
printMemoryAddress(&stringValue) // 0x000000016b8a78c8
printMemoryAddress(&stringValue2) // 0x000000016b8a78b8
}
}
- Stack 메모리 범위 확인
vmmap 61462 | grep Stack
- 첫번째 Stack 영역에 stringValue(16b8a78c8)가 속하는 것 확인
- 두번째 Stack 영역에 stringValue(16b8a78b8)도 속하는 것 확인
struct 타입 메모리 주소
- 동일하게 메모리 주소를 확인
struct StructA {
let a = 0
}
var structValue = StructA()
printMemoryAddress(&structValue) // 0x000000016af9f8d0
- 예상한대로 Struct는 value type이므로 0번 Stack 영역에 할당
class 타입
- 동일하게 메모리 주소를 확인
class ClassA {
let a = 0
}
var classInstance = ClassA()
printMemoryAddress(&classInstance) // 0x000000016b97b8c0
- class는 reference type인데도 Stack 영역에 저장됨을 확인
- heap에 할당되지 않는 이유는?
- 주의) 위에서 살펴본 주소는 classInstance의 주소가 아닌, 참조의 주소를 본 것
- reference type 메모리 관리는 stack영역에 reference 주소를 저장하고, 실제 주소는 heap에 저장됨을 유의
func printHeapMemoryAddress<T>(_ o: T) {
let pointer = UnsafeMutableRawPointer(mutating: Unmanaged.passUnretained(o as AnyObject).toOpaque())
print(pointer)
}
printHeapMemoryAddress(classInstance) // 실제 주소: 0x0000000128956c50
- Stack영역에 주소가 존재하지 않는다는 것을 확인
- Heap영역에 할당된 메모리 주소 영역을 확인해야 하므로 grep vmmap | grep MALLOC 으로 확인
vmmap 62250 | grep MALLOC
- 중간에 MALLOC_TINY라는 곳의 범위에 속하는 것 확인이 가능
- TINY개념은 다음 포스팅 글에서 계속
class타입을 가지고 있는 struct 타입 메모리 주소
- 동일하게 메모리 주소를 확인
class ClassA {
var a = 0
}
struct StructNestedClass {
var a = ClassA()
}
var structValueNestedClass = StructNestedClass()
printMemoryAddress(&structValueNestedClass) // 주소: 0x000000016dcbb8d0
printMemoryAddress(&(structValueNestedClass.a)) // reference 주소: 0x000000016dcbb8d0
printHeapMemoryAddress(structValueNestedClass) // (struct는 class타입이 아니므로 crash)
printHeapMemoryAddress(structValueNestedClass.a) // 주소: 0x0000000153c26710
- vmmap을 통해 stack영역 메모리 확인
- structValueNestedClass 객체는 첫번째 스택 영역범위에 속하므로, Stack영역에 저장
- structValueNestedClass.a 참조 타입 메모리 주소 역시도 Stack영역에 저장
- structValueNestedClass.a 값 자체는 예상한대로 Stack영역에 저장되지 않음
- Heap 영역을 확인
- structValueNestedClass.a 값 자체는 Heap 영역에 할당된것을 확인
vmmap 63090 | grep MALLOC
struct타입을 가지고 있는 class 타입 메모리 주소
- 동일하게 메모리 주소를 확인
class ClassNestedStruct {
var a = StructA()
}
struct StructA {
let a = 0
}
var classNestedStruct = ClassNestedStruct()
printMemoryAddress(&classNestedStruct) // 주소: 0x000000016b5cb8c0
printMemoryAddress(&(classNestedStruct.a)) // 주소: 0x000000014e14fcc0
printHeapMemoryAddress(classNestedStruct) // 주소: 0x000000014e14fcb0
printHeapMemoryAddress(classNestedStruct.a) // (crash: a는 struct이므로)
- 메모리 할당 확인
- class 인스턴스 참조 메모리 위치는 예상대로 Stack에 할당
- class 인스턴스안에 있는 struct 타입 변수(classNestedStruct.a)는 heap에 할당
- class 인스턴스는 예상대로 heap에 할당
- 알게된 점
- class 인스턴스안에 있는 struct 타입 변수(classNestedStruct.a)는 heap에 할당
- struct타입 변수가 곧바로 heap에 할당되는 이유?
- struct를 감싸고 있는 class는 동적으로 메모리를 관리할 수 있는 reference 타입이므로 struct도 reference 타입처럼 관리되는 것
- class 인스턴스 안에 있는 struct 타입 변수는 class처럼 참조 타입이 존재하지 않으므로, stack에 참조 타입을 두지 못하고 곧바로 heap영역에 저장시키는 것
정리
- String 타입은 Stack 영역에 할당
- class 타입 인스턴스의 참조값은 stack영역에 할당되고, 인스턴스 자체 값은 heap에 할당
- class 타입 인스턴스의 참조값 주소를 출력하는 함수와, class 타입 인스턴스 자체의 주소를 출력하는 함수는 다르므로 주의
- class타입을 가지고 있는 struct 타입 메모리 주소는 stack에 할당
- struct 안의 class 인스턴스 reference 주소는 stack에 할당
- struct 안의 class 인스턴스 값 자체 주소는 heap에 할당
- struct타입을 가지고 있는 class 타입 메모리 주소는 heap에 할당
- class 인스턴스 안에 있는 struct 타입 변수는 heap에 할당
- struct는 reference type(class타입)처럼 stack에 참조 주소를 별도로 두지 못하기 때문에, 곧바로 heap에 실제 타입을 두는 것
* 전체 코드: https://github.com/JK0369/ExMemoryCheck
* 참고
https://gist.github.com/godrm/a2bff14ed36a98aa62f934a6ad426617#file-memorydump-swift
'iOS 응용 (swift)' 카테고리의 다른 글
Comments