๐ฑ WWDC 2024 - [Analyze heap memory] ์ ๋ฆฌ ๋ฐ ๋ฆฌ๋ทฐ
- ์ฑ์ ๋์ ๋ฉ๋ชจ๋ฆฌ์ธ ํ ๋ฉ๋ชจ๋ฆฌ์ ๋ํด ์์ธํ ์์๋ณด๊ธฐ.
- Instruments์ Xcode๋ฅผ ์ฌ์ฉํ์ฌ ์ผ๋ฐ์ ์ธ ํ ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ํ๋จ, ๋ถ์, ํด๊ฒฐ ๋ฐฉ๋ฒ
- ์ฑ์์ ์ผ์์ ๋๋ ์ง์์ ์ธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐ, ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ์ง๋จํ๋ ๊ธฐ์ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก ์๊ฐ
โ ์ ์ฒด ์ธ์ ์์ฝ
์ด ์ธ์ ์ iOS/macOS ์ฑ์์ ํ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ์ฉ๋์ ๋ถ์ํ๊ณ , ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ๋ฅผ ์ง๋จํ๋ฉฐ, ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค. ์ฃผ๋ก Xcode์ Instruments ๋๊ตฌ๋ฅผ ์ฌ์ฉํด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ถ์ ํ๊ณ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค.
๐ฏ ์ธ์ ํต์ฌ ์ฃผ์ (5๊ฐ์ง)
1. ํ ์ธก์ ํ๊ธฐ
2. ์ผ์์ ์ฆ๊ฐ์ ๋์ํ๊ธฐ
3. ์ง์์ ์ฆ๊ฐ ์ถ์ ํ๊ธฐ
4. ๋ฉ๋ชจ๋ฆฌ ๋์ ์์ ํ๊ธฐ
5. ๋ฐํ์ ์ฑ๋ฅ ๊ฐ์ ํ๊ธฐ
๐ ํ ๋ฉ๋ชจ๋ฆฌ๋?
• ์ฑ์์ ์๋ช ์ด ๊ธด ๊ฐ์ฒด๋ค์ด ์ ์ฅ๋๋ ๋ฉ๋ชจ๋ฆฌ ์์ญ
• ์ง์ ์ ์ผ๋ก๋ malloc, calloc, realloc ๋ฑ์ผ๋ก ํ ๋น
• ๊ฐ์ ์ ์ผ๋ก๋ Swift/Objective-C ์ธ์คํด์ค๋ฅผ ํตํด ํ ๋น๋จ
• ์ด์์ฒด์ ์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ํ์ด์ง (ํด๋ฆฐ, ๋ํฐ, ์ค์)๋ก ๊ตฌ์ฑ
- ์ฑ์ด ์์๋๋ฉด ์์ฒด์ ์ธ ๋น ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ ๊ณต๊ฐ์ ๋ฐ์
- ์ฑ์ด ์คํ๋ ๋ ์์คํ ์ ๋ฉ์ธ ์คํ ํ์ผ, ์ฐ๊ฒฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ/ ํ๋ ์์ํฌ๋ฅผ ๋ก๋, ๋์คํฌ์์ ์ฝ๊ธฐ ์ ์ฉ ๋ฆฌ์์ค ์์ญ์ ๋งคํ
- ์คํ ์ค์ ์ฑ์ ๊ฐ ์ค๋ ๋์ ๋ก์ปฌ ๋ฐ ์์ ๋ณ์๋ฅผ ์ํ ์คํ ์์ญ์ ์ฌ์ฉ
- ๋์ ์ด๊ณ ์๋ช ์ด ๊ธด ๋ฉ๋ชจ๋ฆฌ๋ ํ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ ๋ฐฐ์น
- ์ฌ๋ฌ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ผ๋ก ๊ตฌ์ฑ๋จ
- ๊ฐ ์์ญ์ ๊ฐ๋ณ ํ ํ ๋น์ผ๋ก ๋๋จ.
- ์ด์์ฒด์ ์ 16kb ๋ฉ๋ชจ๋ฆฌ ํ์ด์ง๋ก ๊ตฌ์ฑ๋จ.
- ์ํ๋ ํด๋ฆฐ, ๋ํฐ ๋๋ ์ค์
- ๋ํฐ์ ์ค์๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ํฌํจ๋จ.
- ํ์ด ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๋๋ถ๋ถ์ ์ฐจ์ง
๐ ๏ธ ํ ๋ฉ๋ชจ๋ฆฌ ์ธก์ ๋๊ตฌ
1. Xcode ๋ฉ๋ชจ๋ฆฌ ๋ณด๊ณ ์
• ์ฑ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ถ์ด ํ์ธ ๊ฐ๋ฅ
• ๊ฐ์์ค๋ฌ์ด ๋ฉ๋ชจ๋ฆฌ ์ฆ๊ฐ ๊ฐ์ง์ ์ ์ฉ
• ๋๊ท๋ชจ ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ์ ์ผ๋ถ ์ต๊ทผ ๊ธฐ๋ก์ ๋ณด์ฌ์ฃผ์ง๋ง ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ๋ ์ด์ ๋ ์๋ ค์ฃผ์ง ๋ชปํจ.
2. Xcode ๋ฉ๋ชจ๋ฆฌ ๊ทธ๋ํ ๋๋ฒ๊ฑฐ
• ํ์ฌ ์ฑ์ ๋ฉ๋ชจ๋ฆฌ ์ค๋ ์ท์ ์บก์ฒํ๊ณ ๊ฐ์ฒด ๊ฐ ์ฐธ์กฐ ํ์ธ ๊ฐ๋ฅ
• ๋์๋ ์ํ ์ฐธ์กฐ ์ ์๊ฐ์ ์ผ๋ก ์ถ์ ๊ฐ๋ฅ
• MallocStackLogging์ ์ฌ์ฉํ๋ฉด ๊ฐ ํ ๋น์ ์ญ์ถ์ ์ด ํฌํจ๋จ
• ํน์ ํ ๋น์ ์ง์คํด์ผ ํ ๋ ์ ์ฉ, xcode์ ๋๋ฒ๊ทธ ๋ง๋์์ ๋ฐ๋ก ์ก์ธ์ค ํ ์ ์์
3. Instruments - Allocations
• ์๊ฐ ํ๋ฆ์ ๋ฐ๋ฅธ ๋ฉ๋ชจ๋ฆฌ ํ ๋น/ํด์ ์ถ์
• ์ฝ ํธ๋ฆฌ ๋ฐ ์์ฑ ์์น ๋ถ์ (ํต๊ณ์ ์ฝ ํธ๋ฆฌ๋ฅผ ์ง๊ณํด ๋ค์ ์ฝ๋๋ก ์ถ์ )
• leaks ๋๊ตฌ๋ ์ฑ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ์ค๋ ์ทํด ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๊ฐ์ง
4. Command-line ๋๊ตฌ๋ค
• leaks, heap, vmmap, malloc_history
• macOS ๋ฐ ์๋ฎฌ๋ ์ดํฐ ํ๋ก์ธ์ค๋ฅผ ์ง์ ๋ถ์ํ๊ฑฐ๋ ์ด๋ฏธ ์บก์ฒ๋ ๋ฉ๋ชจ๋ฆฌ ๊ทธ๋ํ๋ก ๋ฌธ์ ๋ฅผ ์กฐ์ฌํ ์ ์์.
๐ ๋ฉ๋ชจ๋ฆฌ ์ผ์์ ์ฆ๊ฐ ๋ถ์
๋ฌธ์ :
• ๊ฐค๋ฌ๋ฆฌ ์ด๊ธฐ → ๋ฉ๋ชจ๋ฆฌ 1GB ๊ธ์ฆ (xcode ๋ณด๊ณ ์) → ์ฑ ์ข ๋ฃ ์ํ
Allocations ๋๊ตฌ๋ฅผ ํ์ฉ:
• Product -> Profile ๋ฉ๋ด -> ์ฑ์ ๋ฆด๋ฆฌ์ฆ ๋น๋ ์ํ -> ์ฑ์ด ๋์์ผ๋ก ์ ํ๋ ์ํ์์ Instruments๊ฐ ์ด๋ฆผ -> ํ๋กํ์ผ๋งํ ํ ํ๋ฆฟ์ ์ ํํ๋ผ๋ ๋ฉ์์ง ํ์ -> allocations ์ ํ -> allocations(ํ๊ณผ VM ์ด๋ฒคํธ๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ธฐ๋กํ์ฌ ํ๋์ ์ค์๊ฐ์ผ๋ก ํ์ธ ๊ฐ๋ฅ), VM Tracker(์ฃผ๊ธฐ์ ์ผ๋ก ์ค๋ ์ท์ ์์ฑํด ๋ชจ๋ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ธก์ ๊ฐ๋ฅ)์์ allocations ์ ํ -> ์ผ์ชฝ ์๋จ์ Record ๋ฒํผ์ผ๋ก ์ถ์
์ฑ์ ๋ฉ๋ชจ๋ฆฌ ๊ธ์ฆ์ด ์์ข์ ์ด์ :
• ๋ฉ๋ชจ๋ฆฌ ์๋ฐ ์ ๋ฐ
• ๋ํฐ ๋ฉ๋ชจ๋ฆฌ ๊ต์ฒด ๋ฐ ์์ถ, ์ฝ๊ธฐ์ ์ฉ ๋ฉ๋ชจ๋ฆฌ ํ๊ธฐ, ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ์ข ๋ฃ
• ์ต์ ์ ๊ฒฝ์ฐ ์ฑ ์ข ๋ฃ
๋ฉ๋ชจ๋ฆฌ ๊ธ์ฆ ์ถ์ :
1. ํน์ ์คํ์ดํฌ์์ ์ต์ ์ ์์ ์ต๊ณ ์ ์ ์๋ Created & Still Living ํ ๋น ์ฐพ๊ธฐ
2. ์ ์ฒด์ ์ผ๋ก ํฐ ๋ฒ์๋ฅผ ์ ํํด ํด๋น ๋ฒ์์์ Created & Destroyed ๋ ๋ชจ๋ ํ ๋น ์ฐพ๊ธฐ
func makeThumbnail(from photoURL: URL) -> PhotoThumbnail {
validate(url: photoURL)
var coreImage = CIImage(contentsOf: photoURL)!
let sepiaTone = CIFilter.sepiaTone()
sepiaTone.inputImage = coreImage
sepiaTone.intensity = 0.4
coreImage = sepiaTone.outputImage!
let squareSize = min(coreImage.extent.width, coreImage.extent.height)
coreImage = coreImage.cropped(to: CGRect(x: 0, y: 0, width: squareSize, height: squareSize))
let targetSize = CGSize(width:64, height:64)
let scalingFilter = CIFilter.lanczosScaleTransform()
scalingFilter.inputImage = coreImage
scalingFilter.scale = Float(targetSize.height / coreImage.extent.height)
scalingFilter.aspectRatio = Float(Double(coreImage.extent.width) / Double(coreImage.extent.height))
coreImage = scalingFilter.outputImage!
let imageData = context.generateImageData(of: coreImage)
return PhotoThumbnail(size: targetSize, data: imageData, url: photoURL)
}
func loadThumbnails(with renderer: ThumbnailRenderer) {
for photoURL in urls {
renderer.faultThumbnail(from: photoURL)
}
}
์์ : ํ์ฌ ๋ ์ง ์ถ๋ ฅ - ์๋๋ฆด๋ฆฌ์ฆ๋ ๋ฌธ์์ด๋ ์์ฑ, ํ์ฌ ์๋ ๋ฆด๋ฆฌ์ฆ ๋ฒ์๊ฐ ๋๋ ๋ ๊น์ง ํ์ ๋จ์์๊ฒ ๋จ
print("Now is \(Date.now)") // Produces autoreleased .description String
์์ธ ๋ถ์:
• @autoreleasepool ๋ฏธ์ฌ์ฉ์ผ๋ก ๋ฃจํ ๋ด ์์ ๊ฐ์ฒด๊ฐ ๋ฐ๋ณต ๋์
• makeThumbnail() ๋ด ๋ฐ๋ณต ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์ ๊ฐ์ฒด ํด์ ๊ฐ ์ง์ฐ๋จ
ํด๊ฒฐ:
• ๋ฃจํ ๋ด๋ถ์ autoreleasepool {} ์ถ๊ฐ
• ๋ฐ๋ณต๋ง๋ค ๋ฉ๋ชจ๋ฆฌ ํด์ ๋ฅผ ์ ๋
func loadThumbnails(with renderer: ThumbnailRenderer) {
for photoURL in urls {
autoreleasepool {
renderer.faultThumbnail(from: photoURL)
}
}
}
- ๋ฃจํ ๋ณธ๋ฌธ์ ์๋ ๋ฆด๋ฆฌ์ฆ ํ ๋ฒ์๋ฅผ ์ถ๊ฐํด ๊ฐ ๋ฐ๋ณต ํ์ ๊ฐ์ฒด๋ฅผ ๋น์ฐ๋๋ก !
- ๋๋ถ๋ถ์ ํ๋กํ์ผ๋ง์์๋ ์ ํํ ํ์ด๋ฐ์ ์ํด ๋ฆด๋ฆฌ์ฆ ๋น๋๋ฅผ ์ค์ ๊ธฐ๊ธฐ์์ ์คํํ๋ ๊ฒ์ด ์ค์.
- ํ์ง๋ง ํ ๋ถ์์ ๊ฒฝ์ฐ ์๋ฎฌ๋ ์ดํฐ ํ๊ฒฝ์ด ๋์์ ๋ ๊ฐ๊น์์ ๋ฉ๋ชจ๋ฆฌ ํ๋กํ์ผ๋ง์ ์ฌ์ฉํด๋ ๊ด์ฐฎ์.
๐ ์ง์์ ๋ฉ๋ชจ๋ฆฌ ์ฆ๊ฐ (Memory Leak ์๋)
๋ฌธ์ :
• ๊ฐค๋ฌ๋ฆฌ๋ฅผ ์ด ๋๋ง๋ค ์ ์ง์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ์ฆ๊ฐ (๊ณ๋จ ํํ)
๋ถ์ ๋ฐฉ๋ฒ:
• Instruments - Allocations์ Mark Generation ๊ธฐ๋ฅ์ผ๋ก ์๊ฐ๋๋ณ ํ ์ํ ๊ตฌ๋ถ
• ์ฆ๊ฐ ๊ตฌ๊ฐ ๋ณ ๊ฐ์ฒด ์ ํ ํ์ธ
ํ์ ์ ๋ณด์ ์ฐธ์กฐ ์ค์บ์ ๋ํ ์ดํด :
์ฐธ์กฐ์ 4๊ฐ์ง ํ์
func faultThumbnail(from photoURL: URL) {
// Cache the thumbnail based on url + creationDate
let timestamp = UInt64(Date.now.timeIntervalSince1970) // Bad - caching with wrong timestamp
let cacheKey = CacheKey(url: photoURL, timestamp: timestamp)
let thumbnail = cacheProvider.thumbnail(for: cacheKey) {
return makeThumbnail(from: photoURL)
}
images.append(thumbnail.image)
}
-> faultThumbnail ๋ฉ์๋๊ฐ ์ธ๋ค์ผ์ ์บ์ฑํ๊ณ ์บ์ ๋ฏธ์ค ์ ์ ์ธ๋ค์ผ ์์ฑ
-> URL๊ณผ creationDate๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์บ์ฑํ๊ณ ์๋ค๊ณ ์ฃผ์์ ๋์ด์์ง๋ง, ์ฝ๋๋ฅผ ๋ณด๋ ํ์ฌ ์๊ฐ์ผ๋ก ๋์ด์๋ค.
-> ์ด ๋ฉ์๋๊ฐ ํธ์ถ ๋ ๋๋ง๋ค ํญ์ ์๋ก์ด photoThumbnail์ ์บ์
์์ธ:
• ์ธ๋ค์ผ ์บ์ฑ ์ creationDate๊ฐ ์๋ ํ์ฌ ์๊ฐ์ ํค๋ก ์ฌ์ฉ
• ์บ์ ๋ฏธ์ค๊ฐ ๋ฐ๋ณต๋๋ฉฐ ๋ฉ๋ชจ๋ฆฌ ๋์
func faultThumbnail(from photoURL: URL) {
// Cache the thumbnail based on url + creationDate
let timestamp = cacheKeyTimestamp(for: photoURL) // Fixed - caching with correct timestamp
let cacheKey = CacheKey(url: photoURL, timestamp: timestamp)
let thumbnail = cacheProvider.thumbnail(for: cacheKey) {
return makeThumbnail(from: photoURL)
}
images.append(thumbnail.image)
}
ํด๊ฒฐ:
• ์ ํํ ํ์ผ ์์ฑ ์๊ฐ์ผ๋ก ์บ์ฑ ํค ์ค์ ( ์ค์ ํ์ผ ์์ฑ ๋ ์ง๋ฅผ ๊ธฐ์ค์ผ๋ก ์บ์ฑ )
๐จ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ถ์ ๋ฐ ์์
* reachability ๋๋ฌ ๊ฐ๋ฅ์ฑ ?
ํ์ 3๊ฐ์ง ์ข ๋ฅ์ ๋ฉ๋ชจ๋ฆฌ:
1. useful ์ ์ฉํ ๋ฉ๋ชจ๋ฆฌ: ์ค์ ๋ก ์ฌ์ฉ ์ค , ๋๋ฌ ๊ฐ๋ฅ ๋์ค์ ๋ค์ ์ฌ์ฉํ ๋ฉ๋ชจ๋ฆฌ
2. abandoned ๋ฒ๋ ค์ง ๋ฉ๋ชจ๋ฆฌ: ๋๋ฌ ๊ฐ๋ฅํ์ง๋ง ๋ถํ์ํ๊ฒ ์ ์ง ( ์ค์ ๋ก๋ ๋ค์ ์ฌ์ฉํ์ง ์์ ), ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ํฌํจ, ๋ญ๋น
- ์บ์์ ๋ฐ์ดํฐ ๋๋ฌด ๋ง์ด ์ ์ฅ, ์ฑ๊ธํค์ ํฐ ์ฉ๋์ ๋ฐ์ดํฐ ์ ์ง
3. Leaked ๋๋ฌ ๋ถ๊ฐ๋ฅํ ๋ฉ๋ชจ๋ฆฌ: ์ฐธ์กฐ ์ฌ๋ผ์ง → ๋์ , ์ฌ์ฌ์ฉ ์ ๋ ๋ถ๊ฐ๋ฅ
์์ธ:
• ํด๋ก์ ๋ด self๋ฅผ ๊ฐํ๊ฒ ์บก์ฒ → ์ํ ์ฐธ์กฐ ๋ฐ์
// ...
let renderer = ThumbnailRenderer(style: .vibrant)
let loader = ThumbnailLoader(bundle: .main, completionQueue: .main)
loader.completionHandler = {
self.thumbnails = renderer.images // implicit strong capture of renderer causes strong reference cycle
}
loader.beginLoading(with: renderer)
// ...
// ...
let renderer = ThumbnailRenderer(style: .vibrant)
let loader = ThumbnailLoader(bundle: .main, completionQueue: .main)
loader.completionHandler = { [weak renderer] in
guard let renderer else { return }
self.thumbnails = renderer.images
}
loader.beginLoading(with: renderer)
// ...
ํด๊ฒฐ:
• ํด๋ก์ ์ [weak self] ๋๋ [unowned self] ์ฌ์ฉ
• ์ต์ ๋ ์ฒ๋ฆฌ๋ฅผ ํตํด ์์ ํ๊ฒ ์ฌ์ฉ
๐ ๋ฐํ์ ์ฑ๋ฅ ๊ฐ์
weak vs unowned ์ฐจ์ด
- unowend ์ฐธ์กฐ๋ ๋ฐ๋์ ๋ฌด์ธ๊ฐ๋ฅผ ๊ฐ๋ฆฌ์ผ์ผ ํ๋ฏ๋ก ๋ฐํ์์ swallow๋ฅผ ์ ์ง
- weak ์ฐธ์กฐ๋ฅผ ๊ฐ์ ๋ก ์ธ๋ํํ๋ ๊ฒ๊ณผ ๋น์ท
- ์ฐธ์กฐ ๋์์ ํ ๋น ํด์ ๋ ์ ์๊ณ , ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น
class ByteProducer {
let data: Data
private var generator: ((Data) -> UInt8)? = nil
init(data: Data) {
self.data = data
generator = defaultAction // Implicitly uses `self`
}
func defaultAction(_ data: Data) -> UInt8 {
// ...
}
}
- defaultAction ๋ฉ์๋์ ํ ๋น๋ ํด๋ก์ ๋ผ๋ ์์ฑ๊ธฐ ์์ฑ์ด ์์. ์๋ฌต์ ์ผ๋ก self ์ฌ์ฉ.
์ฑ๋ฅ ์ต์ ํ ํ
• -whole-module-optimization ํ์ฑํ
• ๊ตฌ์กฐ์ฒด๋ ๋จ์ํํ์ฌ ๋ณต์ฌ ๋น์ฉ ์ค์ด๊ธฐ
• ARC๋ฅผ ์ฐํํ์ง ๋ง ๊ฒ
• Objective-C๋ objc_direct, objc_externally_retained ๋ฑ์ ํ์ฉ
๐ฏ ํต์ฌ ๋ฉ์์ง ์์ฝ
• ํ ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ๋ ์ฑ ์์ ์ฑ๊ณผ ์ฑ๋ฅ ํฅ์์ ํ์
• ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด ๋ฐ๋ณต ์ธก์ ๋ฐ ์ถ์ ์ผ๋ก ํด๊ฒฐ ๊ฐ๋ฅ
• ์๋ ๋ฆด๋ฆฌ์ฆ ํ, ํด๋ก์ ์ ์บก์ฒ ๋ฐฉ์์ ์ฃผ์ํด์ผ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
• weak/unowned ์ ํ์ ์๋ช ์ ๋ฐ๋ผ ๊ฒฐ์
• ํ๋กํ์ผ๋ง๊ณผ ๋๊ตฌ ํ์ฉ ๋ฅ๋ ฅ์ด ๊ฐ๋ฐ์ ์๋ จ๋๋ฅผ ์ข์ฐ