Heap Dump: คืออะไร การวิเคราะห์ฮีปและการกำจัดการรั่วไหลของหน่วยความจำ

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-05-07 เวลาอ่าน: 10 นาที

Heap Dump (การดัมพ์ฮีป) คือภาพถ่ายของหน่วยความจำแบบไดนามิกของแอปพลิเคชันที่มีข้อมูลที่สมบูรณ์เกี่ยวกับออบเจ็กต์ที่ยังมีชีวิตทั้งหมด: คลาส ขนาด การอ้างอิงซึ่งกันและกัน และการเข้าถึงได้จากราก GC Heap Dump เป็นเครื่องมือหลักในการวิเคราะห์การรั่วไหลของหน่วยความจำและการปรับปรุงการใช้ทรัพยากรให้เหมาะสม ตาม Android Developers การวิเคราะห์ heap dump สามารถตรวจจับการรั่วไหลของหน่วยความจำได้ถึง 95% รวมถึงการอ้างอิงแบบวนซ้ำ listener ที่ถูกลืม และการอ้างอิงแบบสแตติกที่ไม่ได้รับการปลดปล่อย

ประเด็นสำคัญ

  • Heap Dump คือภาพถ่ายของหน่วยความจำแบบไดนามิกทั้งหมดของแอปพลิเคชันพร้อมข้อมูลเกี่ยวกับแต่ละออบเจ็กต์และการอ้างอิงระหว่างออบเจ็กต์เหล่านั้น
  • Android Studio Memory Profiler ช่วยให้สามารถจับภาพ heap dump แบบเรียลไทม์สำหรับแอปพลิเคชัน Java และ Kotlin
  • Xcode Instruments มีเครื่องมือ Allocations สำหรับสร้างและวิเคราะห์ heap dump บน iOS/macOS
  • Shallow และ retained size เป็นหน่วยวัดสำคัญ: shallow คือขนาดของออบเจ็กต์本身 retained คือขนาดของออบเจ็กต์บวกกับออบเจ็กต์ทั้งหมดที่มันยึดไว้
  • การวิเคราะห์ heap dump รวมถึงการค้นหาใน dominator tree ออบเจ็กต์ retained ที่ใหญ่ที่สุด และเส้นทางที่สั้นที่สุดไปยังราก GC

Heap dump คืออะไรและจำเป็นเมื่อใด

Heap dump คือการดัมพ์ฮีปของเครื่องเสมือนอย่างสมบูรณ์ — พื้นที่หน่วยความจำที่ออบเจ็กต์ที่สร้างขึ้นแบบไดนามิกทั้งหมดอาศัยอยู่ ใน Java และ Kotlin คือฮีป Dalvik/ART บน Android ใน Swift และ Objective-C คือฮีปที่จัดการโดย ARC บน iOS Heap dump จะบันทึกทุกออบเจ็กต์ คลาส ขนาด ฟิลด์ การอ้างอิงไปยังออบเจ็กต์อื่น และแฟล็กการเข้าถึงได้จากราก GC (ตัวแปรสแต็ก ฟิลด์สแตติก การอ้างอิง JNI)

วัตถุประสงค์หลักของ heap dump คือ การตรวจจับการรั่วไหลของหน่วยความจำ การรั่วไหลเกิดขึ้นเมื่อแอปพลิเคชันยังคงถือการอ้างอิงไปยังออบเจ็กต์ที่ไม่จำเป็นอีกต่อไป ซึ่งป้องกันไม่ให้ถูกเก็บโดยตัวเก็บขยะ สาเหตุทั่วไป: listener เหตุการณ์ที่ไม่ได้รับการยกเลิกการลงทะเบียนเมื่อ activity ถูกทำลาย; singleton ที่มีการอ้างอิงไปยังบริบท; closure ที่จับ self; คอลเลกชันสแตติกที่มีการเพิ่มข้อมูลโดยไม่มีการลบ Heap dump ให้ภาพที่แม่นยำ: ออบเจ็กต์ใด “ยังมีชีวิต” ออบเจ็กต์ใดไม่จำเป็น และใครกำลังอ้างอิงถึงออบเจ็กต์เหล่านั้น

ตาม Google I/O รายงานการขัดข้องของแอป Android มากกว่า 60% เกี่ยวข้องกับ OutOfMemoryError และใน 80% ของกรณี สาเหตุหลักคือการรั่วไหลของหน่วยความจำที่ตรวจพบได้ผ่าน heap dump สำหรับแอป iOS สถานการณ์คล้ายกัน: การรั่วไหลเนื่องจาก retain cycles เป็นหนึ่งในสาเหตุที่พบบ่อยที่สุดของการขัดข้อง ซึ่งระบุผ่านเครื่องมือ Allocations ใน Xcode

เมื่อใดที่จำเป็นต้องใช้ heap dump

ควรทำ heap dump เมื่อมีอาการต่อไปนี้: แอปพลิเคชันใช้หน่วยความจำแบบเชิงเส้นระหว่างการดำเนินการซ้ำ ๆ; หลังจากปิดหน้าจอ หน่วยความจำไม่กลับสู่ระดับพื้นฐาน; เกิด OutOfMemoryError หรือคำเตือนหน่วยความจำบน iOS; แอปพลิเคชันยุติลงเนื่องจากการเกินขีดจำกัดหน่วยความจำ การเก็บ heap dump เป็นประจำเป็นส่วนหนึ่งของโปรโตคอลวัฒนธรรมวิศวกรรมในโครงการมือถือขนาดใหญ่เช่น Instagram และ Spotify

Heap dump ใน Android Studio: การรับและการวิเคราะห์

Android Studio มี Memory Profiler — เครื่องมือในตัวสำหรับจับภาพ heap dump แบบเรียลไทม์ สามารถเข้าถึงได้ผ่าน View → Tool Windows → Profiler หลังจากเปิดแอปพลิเคชัน ให้เลือกเซสชัน ไปที่แท็บ Memory แล้วคลิก Dump Java Heap Android Studio จะหยุดแอปพลิเคชันชั่วคราว ทำการดัมพ์ฮีป ART และโหลดผลลัพธ์สำหรับการวิเคราะห์ ไฟล์ดัมพ์อยู่ในรูปแบบ .hprof — มาตรฐาน HPROF ที่เข้ากันได้กับเครื่องวิเคราะห์หน่วยความจำส่วนใหญ่

หลังจากโหลดดัมพ์ Android Studio จะแสดงตารางออบเจ็กต์พร้อมคอลัมน์: Allocations (จำนวนอินสแตนซ์), Native Size (หน่วยความจำนอกฮีป ART), Shallow Size (หน่วยความจำของออบเจ็กต์เอง), Retained Size (หน่วยความจำของออบเจ็กต์รวมถึงกราฟย่อยทั้งหมด) การกรองตามชื่อคลาส การเรียงลำดับตาม retained size และการค้นหาตามแพ็คเกจช่วยให้คุณค้นหาพื้นที่ที่มีปัญหาได้อย่างรวดเร็ว

kotlin
// การรั่วไหลทั่วไป — listener ที่ไม่ได้ยกเลิกการลงทะเบียนใน onDestroy
class MainActivity : AppCompatActivity() {
    private val sensorManager by lazy {
        getSystemService(SENSOR_SERVICE) as SensorManager
    }
    private val listener = MySensorListener()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sensorManager.registerListener(listener,
            sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),
            SensorManager.SENSOR_DELAY_NORMAL)
    }

    override fun onDestroy() {
        super.onDestroy()
        // ❌ ขาด sensorManager.unregisterListener(listener)
        // → Activity จะไม่ถูก GC, heap dump จะแสดงการรั่วไหล
    }
}

การวิเคราะห์ dominator tree ใน Android Studio

แท็บ Dominator Tree แสดงออบเจ็กต์ที่ยึดหน่วยความจำจำนวนมากที่สุด หากออบเจ็กต์ถูกลบออกจาก dominator tree หน่วยความจำทั้งหมดที่มันยึดไว้จะพร้อมใช้งานสำหรับการเก็บขยะ นี่คือเครื่องมือสำคัญ: แทนที่จะสแกนออบเจ็กต์นับพัน คุณมุ่งเน้นไปที่ 10–20 ออบเจ็กต์ที่ควบคุม 80–90% ของหน่วยความจำ ตาม Google การวิเคราะห์ dominator tree เป็นวิธีที่มีประสิทธิภาพที่สุดในการหาจุดรั่วไหล ซึ่งลดเวลาในการวิเคราะห์จากชั่วโมงเป็นนาที

Heap dump ใน Xcode Instruments: Allocations และ Leaks

Xcode Instruments มีเครื่องมือสองอย่างสำหรับทำงานกับ heap dump: Allocations — การจับภาพดัมพ์ฮีปพร้อมกราฟการใช้แบบเรียลไทม์; Leaks — การค้นหารั่วไหลอัตโนมัติผ่านการวิเคราะห์ retain cycles Allocations แสดงออบเจ็กต์ทั้งหมดในฮีป ขนาด จำนวนการสร้าง (allocations) และการปลดปล่อย (deallocations) ความแตกต่างระหว่างจำนวนการสร้างและการปลดปล่อยสำหรับคลาสเฉพาะบ่งชี้ถึงการรั่วไหลที่อาจเกิดขึ้น

การจับภาพ heap dump ใน Allocations ทำได้ด้วยปุ่ม Snapshot Memory — เครื่องมือจะหยุดแอปพลิเคชันชั่วคราวและทำการดัมพ์แบบสมบูรณ์ หลังจากนั้น มุมมองมาตรฐานจะพร้อมใช้งาน: รายการออบเจ็กต์ตามคลาส ต้นไม้การเรียก (call tree) สำหรับแต่ละออบเจ็กต์ และตัวสร้างรายงาน แตกต่างจาก Android Studio ตรงที่ Xcode ไม่ใช้ .hprof แต่เก็บข้อมูลในรูปแบบ .trace ของตัวเองที่เข้ากันได้กับ Instruments

swift
// การรั่วไหลของ iOS ทั่วไป — retain cycle ผ่าน closure
class NetworkManager {
    var onComplete: ((Data) -> Void)?

    func startRequest() {
        // ❌ Closure จับ self — retain cycle
        onComplete = { data in
            self.process(data)
        }
    }
    func process(_ data: Data) {}
}

เครื่องมือ Leaks ตรวจจับ retain cycles และการรั่วไหลโดยอัตโนมัติผ่านการวิเคราะห์กราฟการอ้างอิง มันทำเครื่องหมายออบเจ็กต์ที่รั่วไหลด้วยไอคอนสีม่วงและแสดงเส้นทางไปยังราก (GC root) เพื่อกำจัด retain cycle เพียงเพิ่ม [weak self] หรือ [unowned self] ในการจับภาพ closure การรันเครื่องมือ Leaks เป็นประจำเป็นขั้นตอนบังคับของไปป์ไลน์ CI ในทีมที่ใช้ Swift สำหรับการพัฒนา iOS

swift
// การแก้ไข — การอ้างอิงแบบอ่อนไปยัง self
onComplete = { [weak self] data in
    guard let self else { return }
    self.process(data)
}

Shallow size, retained size และ dominator tree

เพื่อวิเคราะห์ heap dump อย่างถูกต้อง จำเป็นต้องเข้าใจหน่วยวัดสำคัญสามประการ Shallow size คือปริมาณหน่วยความจำที่ออบเจ็กต์ครอบครองโดยตรง: ฟิลด์ ส่วนหัว และการจัดตำแหน่ง สำหรับออบเจ็กต์ Java/Kotlin ทั่วไป shallow size คือ 16–40 ไบต์ Retained size คือ shallow size ของออบเจ็กต์บวกกับผลรวม shallow size ของออบเจ็กต์ทั้งหมดที่สามารถเข้าถึงได้ผ่านออบเจ็กต์นี้เท่านั้น Retained size แสดงผลกระทบที่แท้จริงของออบเจ็กต์ต่อการใช้หน่วยความจำ

หน่วยวัดคำอธิบายตัวอย่าง
Shallow sizeขนาดของออบเจ็กต์本身เป็นไบต์Bitmap (100×100) = 40,016 B
Retained sizeShallow size + ทุกสิ่งที่มันยึดไว้Activity พร้อม View Tree = 2–5 MB
Deep sizeRetained size + ออบเจ็กต์ที่ซ้อนจากกราฟอื่นScrollView พร้อมอะแดปเตอร์ = 10–50 MB

Dominator tree คือโครงสร้างที่แต่ละออบเจ็กต์อ้างอิงถึง “ผู้ครอบงำ” ของมัน — ออบเจ็กต์ที่ควบคุมการเข้าถึงได้ของมัน หากผู้ครอบงำถูกลบ ออบเจ็กต์ทั้งหมดในต้นไม้ย่อยของมันจะกลายเป็นขยะ การวิเคราะห์ dominator tree เป็นวิธีที่เร็วที่สุดในการค้นหาว่าออบเจ็กต์ใดยึดหน่วยความจำมากที่สุด ตาม Eclipse MAT 90% ของการรั่วไหลถูกตรวจพบโดยการตรวจสอบ top-20 dominator tree ภายใน 5 นาที

การวิเคราะห์การรั่วไหลของหน่วยความจำผ่าน heap dump

กระบวนการวิเคราะห์การรั่วไหลผ่าน heap dump ประกอบด้วยหลายขั้นตอน ขั้นตอนที่ 1: ดำเนินการที่ควรจะปลดปล่อยหน่วยความจำ ขั้นตอนที่ 2: เรียก GC และทำ heap dump ขั้นตอนที่ 3: ค้นหาออบเจ็กต์ที่ควรถูกทำลาย ขั้นตอนที่ 4: สำหรับออบเจ็กต์ที่น่าสงสัย ให้รัน Path to GC Roots — ห่วงโซ่การอ้างอิงที่ทำให้ออบเจ็กต์ยังมีชีวิตอยู่ การอ้างอิงสุดท้ายในห่วงโซ่คือสาเหตุของการรั่วไหล

Path to GC Roots

ฟังก์ชัน Path to GC Roots มีอยู่ใน Android Studio Profiler, Eclipse MAT และ Xcode Instruments มันแสดงห่วงโซ่การอ้างอิงที่สั้นที่สุดจากราก GC ไปยังออบเจ็กต์ที่มีปัญหา โดยการยกเว้นการอ้างอิงแบบอ่อน (weak) และแบบนิ่ม (soft) คุณจะได้เฉพาะการอ้างอิงแบบแข็ง (strong) — การอ้างอิงที่ป้องกันการเก็บขยะจริง ๆ ตาม Square Engineering 70% ของการรั่วไหลในแอป Android เกิดจากเพียงสองรูปแบบ: การอ้างอิงแบบสแตติกไปยัง Activity หรือ Context และ listener ที่ลงทะเบียนแล้วแต่ไม่ได้ยกเลิกการลงทะเบียน

kotlin
// ตัวอย่างการรั่วไหลผ่านการอ้างอิงแบบสแตติก
object AppCache {
    private val cache = mutableMapOf<String, Any>()

    fun storeActivityReference(activity: Activity) {
        cache["current_activity"] = activity // ❌ การรั่วไหล!
    }
}

// การแก้ไข: การอ้างอิงแบบอ่อน
object AppCacheFixed {
    private val cache = mutableMapOf<String, WeakReference<Any>>()
}

การเปรียบเทียบ heap dump สองครั้ง

เทคนิค โหมดเปรียบเทียบ เป็นหนึ่งในวิธีการที่มีประสิทธิภาพมากที่สุดในการค้นหารั่วไหล ทำ heap dump ก่อนและหลังการดำเนินการซ้ำ ๆ เปรียบเทียบจำนวนอินสแตนซ์ของคลาสสำคัญ: หากจำนวน Activity เพิ่มขึ้นแม้ว่า activity ทั้งหมดจะถูกปิด — นั่นคือการรั่วไหล Android Studio และ Eclipse MAT รองรับการเปรียบเทียบดัมพ์อัตโนมัติพร้อมการเน้นความแตกต่าง ตาม Google การเปรียบเทียบดัมพ์ช่วยให้พบการรั่วไหลที่มองไม่เห็นในการวิเคราะห์ครั้งเดียวเนื่องจากผลสะสม

คำแนะนำเชิงปฏิบัติเพื่อลดการใช้หน่วยความจำ

จากการวิเคราะห์ heap dump ในโครงการจริง ได้พัฒนาแนวทางปฏิบัติที่พิสูจน์แล้วสำหรับการปรับหน่วยความจำให้เหมาะสม ใช้ WeakReference สำหรับแคช การเรียกกลับ และการอ้างอิงบริบทในออบเจ็กต์ที่มีอายุยืน ยกเลิกการลงทะเบียน listener ใน onPause/onDestroy สำหรับ Android และ deinit สำหรับ iOS หลีกเลี่ยงคอลเลกชันสแตติกขนาดใหญ่ — หากจำเป็น ให้ใช้ LruCache ด้วยขีดจำกัดขนาด ปรับ Bitmap ให้เหมาะสม: โหลดภาพด้วย inSampleSize ที่ถูกต้อง ใช้ Glide หรือ Picasso พร้อมแคชดิสก์

การทำโปรไฟล์หน่วยความจำระหว่างการพัฒนา

รวมการจับภาพ heap dump เป็นประจำใน ไปป์ไลน์ CI ของคุณ ตั้งค่างานที่รันการทดสอบ UI ที่ถูกตรวจจับ ดำเนินการสถานการณ์ผู้ใช้หลัก และเปรียบเทียบ heap dump กับเส้นพื้นฐาน หาก retained size เพิ่มขึ้นมากกว่า 5% จากเส้นพื้นฐาน บิลด์จะถูกทำเครื่องหมายเป็นการถดถอย แนวทางนี้ปฏิบัติใน Airbnb, Uber และบริษัทอื่น ๆ ที่มีข้อกำหนดด้านคุณภาพสูง ตาม Uber Engineering การนำการวิเคราะห์ heap dump อัตโนมัติใน CI ไปใช้ลดบักที่เกี่ยวข้องกับหน่วยความจำลง 70% ในหนึ่งไตรมาส

groovy
// ตัวอย่างงาน Gradle สำหรับ heap dump อัตโนมัติใน CI
task profileMemory(type: Exec) {
    commandLine 'adb', 'shell',
        'am start -n com.example/.MainActivity'
    // กำลังรอการโหลด
    doLast {
        exec { commandLine 'adb', 'shell',
            'am broadcast -a com.example.DUMP_HEAP' }
    }
}

คำถามที่พบบ่อย

ความแตกต่างระหว่าง shallow size และ retained size คืออะไร?

Shallow size คือขนาดของออบเจ็กต์本身 (ฟิลด์ + ส่วนหัว) Retained size คือขนาดของออบเจ็กต์บวกกับออบเจ็กต์ทั้งหมดที่จะกลายเป็นขยะหากมันถูกลบ Retained size เป็นตัวบ่งชี้หลักของผลกระทบของออบเจ็กต์ต่อการใช้หน่วยความจำ

วิธีทำ heap dump บนอุปกรณ์ Android จริง?

ผ่าน Android Studio Profiler เลือกอุปกรณ์และกระบวนการ คลิก Dump Java Heap หรือผ่านบรรทัดคำสั่ง: adb shell am dumpheap PID /sdcard/dump.hprof จากนั้น adb pull

ทำไม heap dump ถึงใหญ่มาก (500 MB+)?

Heap dump รวมออบเจ็กต์ที่ ยังมีชีวิต ทั้งหมด หากแอปพลิเคชันใช้แคช Bitmap หรือประมวลผลข้อมูลขนาดใหญ่ ดัมพ์อาจถึงหลายร้อยเมกะไบต์ กรองตามคลาสหรือใช้ Eclipse MAT เพื่อโหลดเฉพาะดัชนี

สามารถวิเคราะห์ heap dump โดยไม่มี Android Studio ได้หรือไม่?

ได้ ใช้ Eclipse MAT (Memory Analyzer Tool) — เครื่องมือฟรีสำหรับวิเคราะห์ไฟล์ .hprof รองรับ dominator tree, path to GC roots, การเปรียบเทียบดัมพ์ และการตรวจจับการรั่วไหลอัตโนมัติผ่าน Leak Suspects Report

Heap dump ส่งผลต่อประสิทธิภาพของแอปพลิเคชันหรือไม่?

ตัวดัมพ์เอง — ใช่ เพราะการเก็บดัมพ์หยุดเธรดทั้งหมด (stop-the-world) ไม่มีดัมพ์ — ไม่ ควรทำดัมพ์ในสภาพแวดล้อมที่ควบคุม (แท่นทดสอบ CI) ไม่ใช่ในระบบผลิต

สรุป

  • Heap Dump คือภาพถ่ายที่สมบูรณ์ของฮีปแอปพลิเคชันพร้อมข้อมูลเกี่ยวกับแต่ละออบเจ็กต์และความสัมพันธ์ระหว่างออบเจ็กต์เหล่านั้น
  • Android Studio Memory Profiler และ Xcode Instruments Allocations เป็นเครื่องมือจับภาพดัมพ์หลัก
  • Shallow size คือขนาดของออบเจ็กต์本身; retained size คือขนาดของออบเจ็กต์พร้อมกราฟย่อยการพึ่งพาทั้งหมด
  • Dominator tree แสดงออบเจ็กต์ที่ควบคุมหน่วยความจำมากที่สุด
  • Path to GC Roots คือห่วงโซ่การอ้างอิงแบบแข็งที่ป้องกันออบเจ็กต์จากการถูกเก็บขยะ
  • การเปรียบเทียบ heap dump สองครั้ง (ก่อน/หลังการดำเนินการ) เป็นวิธีที่น่าเชื่อถือที่สุดในการตรวจจับการรั่วไหล
  • การทำงานอัตโนมัติ ของการจับภาพและการวิเคราะห์ heap dump ใน CI ป้องกันการถดถอยของหน่วยความจำระหว่างการพัฒนา

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม