ออบเจ็กต์บิตแมปมักเป็นองค์ประกอบเดียวที่ใหญ่ที่สุดซึ่งมีส่วนทำให้แอปพลิเคชันใช้หน่วยความจำที่ใช้มาก ไม่ว่าจะเป็นไอคอนแอป รูปภาพการแจ้งเตือน หรือเนื้อหาสื่อ การจัดการบิตแมปที่ไม่มีประสิทธิภาพอาจทำให้เกิดข้อผิดพลาดหน่วยความจำไม่พอ (OOM) และ แรงกดดันด้านหน่วยความจำทั่วทั้งระบบได้อย่างรวดเร็ว
การกำหนดค่าบิตแมปและข้อมูลพิกเซล
ปริมาณหน่วยความจำที่บิตแมปใช้จะกำหนดโดยขนาด (กว้าง × สูง) และการกำหนดค่า (Bitmap.Config) เป็นหลัก
การกำหนดค่าจะกำหนดจำนวนไบต์ที่ใช้เพื่อแสดงแต่ละพิกเซล ดังนี้
| การกำหนดค่า | ไบต์ต่อพิกเซล | คำอธิบาย |
|---|---|---|
ALPHA_8 |
1 | เฉพาะช่องอัลฟ่า (ความโปร่งใส) มีประโยชน์สำหรับมาสก์ |
RGB_565 |
2 | แดง (5 บิต), เขียว (6 บิต), น้ำเงิน (5 บิต) ไม่มีเวอร์ชันอัลฟ่า เหมาะสำหรับรูปภาพทึบแสงที่ไม่จำเป็นต้องมีความเที่ยงตรงของสีสูง |
ARGB_8888 |
4 | อัลฟ่า แดง เขียว น้ำเงิน (อย่างละ 8 บิต) ค่าเริ่มต้นและใช้กันมากที่สุด |
RGBA_F16 |
8 | จุดลอยตัวแบบความแม่นยำครึ่งหนึ่ง ใช้สำหรับเนื้อหาที่มีช่วงสีแบบกว้างและ HDR |
HARDWARE |
ไม่มี | จัดเก็บไว้ในหน่วยความจำกราฟิก (gralloc/DMABuf) ดูบิตแมปฮาร์ดแวร์ |
สูตรหน่วยความจำ: Memory (Bytes) = Width × Height × Bytes Per Pixel
เช่น รูปภาพแบบเต็มหน้าจอบนอุปกรณ์ 1080p (1920x1080) ใน ARGB_8888
ใช้พื้นที่: 1920 × 1080 × 4 ไบต์ ≈ 8.3 MB
บิตแมปฮีปเทียบกับบิตแมปที่แชร์
บิตแมปฮีป (ฮีปเนทีฟ)
ใน Android รุ่นใหม่ (8.0 ขึ้นไป) ระบบจะจัดเก็บข้อมูลพิกเซลของบิตแมปไว้ในฮีปแบบเนทีฟ ขณะที่จัดเก็บออบเจ็กต์ Wrapper ขนาดเล็กไว้ในฮีป Java เท่านั้น
เมื่อแอปต้องการแสดงรูปภาพ โดยปกติแล้วระบบจะถอดรหัสจากไฟล์รูปภาพที่บีบอัด เป็นบิตแมปและจัดเก็บไว้ในฮีป
บิตแมปที่แชร์ (ashmem/memfd)
เมื่อมีการโอนบิตแมประหว่างกระบวนการ (เช่น ผ่าน Binder ไปยัง SystemUI
สําหรับการแจ้งเตือน) Android จะหลีกเลี่ยงการคัดลอกข้อมูลพิกเซลโดยใช้หน่วยความจำที่ใช้ร่วมกัน (ashmem หรือ memfd)
คุณสามารถคัดลอกอินสแตนซ์ Bitmap ไปยังหน่วยความจำที่ใช้ร่วมกันได้โดยชัดแจ้งด้วยการเรียกใช้
Bitmap.asShared()
หรือโดยนัยหากมีการใส่ Bitmap ไว้ใน Parcel (โดยปกติคือการเพิ่มบิตแมปลงใน Parcelable เช่น Bundle) และส่งผ่าน Binder IPC
เมื่อส่งบิตแมปที่แชร์ผ่าน Binder IPC ระบบจะไม่คัดลอกข้อมูลพิกเซล แต่จะทำซ้ำตัวอธิบายไฟล์ที่อ้างอิงถึงรีเจียนหน่วยความจำที่แชร์ไปยังกระบวนการของผู้รับ อาจมีการแชร์รีเจียนหน่วยความจำพื้นฐาน ระหว่างหลายกระบวนการ และระบบจะไม่ปล่อยหน่วยความจำจนกว่าจะปิดตัวอธิบายไฟล์ทั้งหมด ที่อ้างอิงถึงหน่วยความจำนั้น
บิตแมปที่เปลี่ยนแปลงได้กับบิตแมปที่เปลี่ยนแปลงไม่ได้
- บิตแมปที่เปลี่ยนแปลงได้: แก้ไขได้หลังจากสร้าง (เช่น ผ่าน
Canvas) ต้องมีการจัดสรรหน่วยความจำส่วนตัวของตัวเองเสมอ หากคัดลอก Bitmap ที่เปลี่ยนแปลงได้ จะต้องทำการคัดลอกแบบลึก (สำเนาที่ 2 ของข้อมูลพิกเซลทั้งหมด) - บิตแมปที่เปลี่ยนแปลงไม่ได้: เปลี่ยนแปลงไม่ได้ ซึ่งช่วยให้เพิ่มประสิทธิภาพได้ เช่น
การแชร์บัฟเฟอร์หน่วยความจำพื้นฐานเดียวกันระหว่าง
Bitmapอินสแตนซ์ต่างๆ โดยทั่วไปแล้ว บิตแมปที่โหลดจากทรัพยากร APK (BitmapFactory) จะ เปลี่ยนแปลงไม่ได้
การจัดการบิตแมปอย่างมีประสิทธิภาพ
การจัดกลุ่มและการนำบิตแมปมาใช้ซ้ำ
การจัดสรรและยกเลิกการจัดสรรบิตแมปบ่อยๆ จะทำให้เกิดการเปลี่ยนแปลงการจัดสรร ซึ่งบังคับให้ GC ทำงานอย่างต่อเนื่อง ไลบรารีการโหลดรูปภาพที่ใช้กันทั่วไปจะใช้Bitmap Pool
Google ขอแนะนำให้ใช้ Glide เป็นโซลูชัน สำหรับแอปพลิเคชันที่ใช้ Java และ Coil สำหรับ แอปพลิเคชันที่ใช้ Kotlin (โดยเฉพาะเมื่อใช้ Jetpack Compose)
เมื่อไม่ต้องการบิตแมปอีกต่อไป แอปจะเรียกใช้
bitmap.recycle() หรือส่งคืนไปยังพูลแทนที่จะปล่อยให้ GC จัดการ ครั้งถัดไปที่ต้องใช้บิตแมปที่มี
ขนาดและการกำหนดค่าเดียวกัน พูลจะให้บัฟเฟอร์ที่มีอยู่
เพื่อหลีกเลี่ยงการจัดสรรใหม่
บิตแมปฮาร์ดแวร์
Bitmap.Config.HARDWARE ช่วยให้คุณจัดเก็บข้อมูลพิกเซลในหน่วยความจำ
กราฟิก (DMABuf) ได้โดยตรง
- ข้อดี
- การประหยัดหน่วยความจำ: ไม่ใช้ฮีปของแอปพลิเคชันหรือฮีปแบบเนทีฟ แต่ใช้หน่วยความจำ GPU บ่อยครั้งที่บิตแมปที่แสดงใน UI ของแอปต้องได้รับการคัดลอกไปยังหน่วยความจำ GPU อยู่ดี ดังนั้นวิธีนี้จึงช่วยประหยัดการดำเนินการคัดลอกและค่าใช้จ่ายด้านหน่วยความจำเพิ่มเติม
- ประสิทธิภาพ: วาดได้รวดเร็วมากเนื่องจากข้อมูลอยู่ใน GPU อยู่แล้ว
- ข้อเสีย
- เปลี่ยนแปลงไม่ได้: แก้ไขบิตแมปของฮาร์ดแวร์ไม่ได้
- การอ่านกลับช้า: การเข้าถึงพิกเซลจาก CPU (เช่น
getPixel()) มีค่าใช้จ่ายสูงมาก - การระบุแหล่งที่มา: ติดตามได้ยากขึ้นในเครื่องมือมาตรฐาน เช่น AHAT (ดูด้านล่าง)
ข้อผิดพลาดที่พบบ่อยเกี่ยวกับหน่วยความจำบิตแมป
แม้ว่าคุณจะใช้การกำหนดค่าบิตแมปที่ทันสมัย แต่รูปแบบที่เกิดซ้ำหลายรูปแบบใน วิธีถอดรหัสและกำหนดเวลาบิตแมปอาจทำให้หน่วยความจำเพิ่มขึ้นอย่างมาก
การถอดรหัสบิตแมปที่ขยายขนาดมากเกินไป
รูปภาพความละเอียดเต็ม 4000 × 3000 พิกเซลใช้พื้นที่ 48 MB ใน ARGB_8888 การถอดรหัส
รูปภาพแบบเต็มเพื่อแสดงภายในภาพขนาดย่อขนาด 200 × 150 พิกเซลจะทำให้
สิ้นเปลืองบัฟเฟอร์พิกเซลที่จัดสรรไปกว่า 99%
เมื่อถอดรหัสรูปภาพโดยตรงด้วย ImageDecoder หรือ
BitmapFactory ให้ลดขนาดความละเอียดระหว่างการส่งผ่านการถอดรหัสเพื่อให้ตรงกับ
ขนาดของมุมมองเป้าหมายโดยใช้ ImageDecoder.setTargetSize() หรือ
BitmapFactory.Options.inSampleSize ไลบรารีการโหลดรูปภาพ เช่น Glide และ Coil จะทำการลดขนาดความละเอียดนี้โดยอัตโนมัติเมื่อคุณระบุขนาดมุมมองเป้าหมายที่จำกัด
เช่น เมื่อถอดรหัสบิตแมปด้วย ImageDecoder ให้ส่ง OnHeaderDecodedListener ที่ปรับขนาดมิติข้อมูลเอาต์พุตลงมาเป็นขนาดมุมมองเป้าหมาย
val source = ImageDecoder.createSource(resources, R.drawable.high_res_photo)
val bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
if (info.size.width > targetWidth || info.size.height > targetHeight) {
decoder.setTargetSize(targetWidth, targetHeight)
}
}
การคงความสนใจของผู้ชมที่ดูพร้อมกันสูงจากการถอดรหัสแบบคู่ขนาน
แม้ว่าบิตแมปแต่ละรายการจะมีขนาดที่เหมาะสมและมีอายุสั้น แต่การถอดรหัสรูปภาพจำนวนมากพร้อมกันอาจทำให้หน่วยความจำเพิ่มขึ้นอย่างมาก เช่น หากหน้าจอผู้จัด หรือแกลเลอรีส่งงาน 30 งานใน Thread Pool ที่ไม่มีขอบเขตเพื่อถอดรหัส ไอคอนหรือภาพปกพร้อมกัน บัฟเฟอร์พิกเซลที่ไม่ได้บีบอัดทั้ง 30 รายการและ บัฟเฟอร์ชั่วคราวของตัวถอดรหัสจะใช้ RAM พร้อมกัน
การคงผู้ใช้พร้อมกันสูงนี้จะเพิ่มร่องรอยฮีปดั้งเดิมสูงสุดและอาจทําให้เกิดการlmkdหยุดทำงานก่อนที่กลุ่มจะเสร็จสิ้น จำกัดการทำงานพร้อมกันของการถอดรหัส
ด้วยกลุ่มเธรดแบบจำกัด เซมาฟอร์ หรือตัวจัดส่งโครูทีน เช่น
Dispatchers.IO.limitedParallelism(2) เพื่อให้มีการถอดรหัสบิตแมปเพียงไม่กี่รายการในครั้งเดียว
บิตแมปชั่วคราวที่ไม่ได้รีไซเคิลในลูปการประมวลผลเฟรม
ใน Android 8.0 ขึ้นไป ออบเจ็กต์ Wrapper ของ Java Bitmap ใช้พื้นที่ในฮีปของ Java เพียงประมาณ 56
ไบต์ ในขณะที่บัฟเฟอร์พิกเซลจะอยู่ในฮีปแบบเนทีฟและอาจใช้พื้นที่หลายเมกะไบต์
คุณสามารถยืนยันการแยกนี้ได้ในเครื่องมือสร้างโปรไฟล์หน่วยความจำของ Android Studio หรือ AHAT ซึ่งแต่ละอินสแตนซ์ของ Bitmap จะแสดงขนาด Java แบบตื้นประมาณ 56 ไบต์ควบคู่ไปกับขนาดแบบเนทีฟหลายเมกะไบต์ และใน dumpsys meminfo ภายใน Native Allocations
(Bitmap (malloced))
ไปป์ไลน์ความถี่สูง เช่น การวิเคราะห์เฟรมกล้อง, OCR หรือลูปการอนุมาน ML มักจะจัดสรรบิตแมปใหม่ในทุกเฟรมโดยการเรียกใช้ ImageProxy.toBitmap() และ Bitmap.createBitmap() เพื่อหมุนหรือครอบตัด
การทิ้งการอ้างอิงเฟรมที่ถูกแทนที่โดยไม่รีไซเคิลอาจทำให้หน่วยความจำเนทีฟเพิ่มขึ้นอย่างรวดเร็ว
ซึ่ง Wrapper ขนาดเล็กของ Java แทบจะไม่เพิ่มการใช้งานฮีปของ Java เลย จึงไม่ทริกเกอร์ระบบจัดการหน่วยความจำที่ไม่ใช้แล้วเร็วพอที่จะป้องกันไม่ให้บัฟเฟอร์พิกเซลแบบเนทีฟหลายร้อยเมกะไบต์สะสมก่อนที่ NativeAllocationRegistry จะเรียกคืน
เมื่อประมวลผลเฟรมในลูปที่แน่น ให้ใช้บัฟเฟอร์ที่จัดสรรไว้ล่วงหน้าซ้ำหากเป็นไปได้ หรือเรียกใช้ bitmap.recycle() อย่างชัดเจนในบิตแมประหว่างกลางชั่วคราว
ทันทีที่แต่ละเฟรมประมวลผลเสร็จ
แบบฝึกหัดภาคปฏิบัติ: การสำรวจบิตแมป
เราจะใช้แอปตัวอย่าง BitmapLab เพื่อสำรวจแนวคิดเหล่านี้
1. การวัดผลด้วย dumpsys meminfo
เปิด BitmapLab แล้วแตะ ALLOCATE 10MB ARGB_8888 จากนั้นเรียกใช้คำสั่งต่อไปนี้
adb shell dumpsys meminfo -s com.android.bitmaplab
ใน Android เวอร์ชันใหม่ ให้มองหาส่วนการจัดสรรหน่วยความจำแบบเนทีฟ ซึ่งจะช่วยให้การระบุแหล่งที่มาของบิตแมปดีกว่าข้อมูลสรุปของแอปทั่วไปมาก
Native Allocations
Count Total(kB)
------ ------
Bitmap (malloced): 1 10240 # <--- 10MB Bitmap data!
Bitmap (nonmalloced): 0 0
- บิตแมป (malloced): บิตแมปที่จัดสรรในฮีปเนทีฟของกระบวนการ ซึ่งเป็นที่อยู่ของบิตแมปมาตรฐานส่วนใหญ่ใน Android 8.0 ขึ้นไป
- บิตแมป (nonmalloced): บิตแมปที่ใช้หน่วยความจำเฉพาะ เช่น บิตแมปฮาร์ดแวร์หรือบิตแมปที่แชร์ (ผ่าน
ashmemหรือmemfd)
หากจัดสรร Shared Bitmap ใน BitmapLab คุณจะเห็นการเปลี่ยนแปลง
ใน Bitmap (nonmalloced) ดังนี้
Native Allocations
Count Total(kB)
------ ------
Bitmap (malloced): 1 10240
Bitmap (nonmalloced): 1 10240 # <--- Shared Bitmap!
การติดตามบิตแมปที่แชร์
ใน Android บางเวอร์ชันและการกำหนดค่าเคอร์เนล dumpsys meminfo ยังให้การติดตามความละเอียดสูงสำหรับบิตแมปที่แมปกับพื้นที่ที่อยู่ของกระบวนการผ่านตัวอธิบายไฟล์ด้วย
โดยค่าเริ่มต้น บิตแมปที่แชร์จะใช้ชื่อทั่วไป ("บิตแมป") หากต้องการเปิดใช้การระบุแหล่งที่มาแบบละเอียด และการติดตามบิตแมปที่ไม่ซ้ำกัน (การระบุบิตแมปที่แชร์ใน กระบวนการต่างๆ) คุณต้องเปิดใช้พร็อพเพอร์ตี้ของระบบต่อไปนี้
adb shell setprop debug.hwui.bitmap_ashmem_long_name true
เมื่อเปิดใช้แล้ว ภูมิภาค ashmem ใน /proc/<pid>/smaps จะมีชื่อที่อธิบายอย่างชัดเจนมากขึ้น
meminfo จะใช้ประโยชน์จากสิ่งนั้น และผลลัพธ์จะมีลักษณะดังนี้
Shared Bitmaps
Count Size(KB)
------ ------
Mapped: 1 10240
Unique: 1 10240
- แมป: ขนาดทั้งหมดของการแมปหน่วยความจำที่เกี่ยวข้องกับบิตแมปทั้งหมด
- ไม่ซ้ำ: ขนาดของบิตแมปที่พิจารณาเฉพาะรายการที่ไม่ซ้ำ (เช่น การแมป 2 รายการขึ้นไปของข้อมูลพิกเซลบิตแมปที่แชร์เดียวกันจะนับเพียงครั้งเดียว)
2. บิตแมปใน AHAT
AHAT มีการแสดงภาพที่ยอดเยี่ยมสำหรับบิตแมป
- ใน BitmapLab ให้จัดสรรบิตแมป 2-3 รายการ
บันทึกฮีปดัมป์ด้วยแฟล็ก
-b(เพื่อรวมข้อมูลบิตแมปดั้งเดิม)adb shell am dumpheap -b png com.android.bitmaplab /data/local/tmp/bitmaps.hprof adb pull /data/local/tmp/bitmaps.hprof . ahat bitmaps.hprofเปิด
localhost:7100แล้วมองหาลิงก์ Bitmaps ในแถบด้านข้างหรือ ค้นหาคลาสBitmapAHAT จะแสดงผลบิตแมปในเบราว์เซอร์จริงๆ ทำให้ระบุได้ง่ายว่ารูปภาพใดใช้หน่วยความจำมากเกินไป

3. แทร็กบิตแมปใน Perfetto
Perfetto สามารถติดตามการจัดสรรบิตแมปและจำนวนได้เมื่อเวลาผ่านไป เคาน์เตอร์เหล่านี้จะ ปล่อยออกมาโดยเฟรมเวิร์ก Android เมื่อเปิดใช้หมวดหมู่ gfx ใน atrace สำหรับ แอปพลิเคชันที่เฉพาะเจาะจง
เริ่มการติดตาม คุณต้องระบุ
gfxหมวดหมู่และกำหนดเป้าหมายแพ็กเกจแอปที่เฉพาะเจาะจงโดยใช้แฟล็ก-aดังนี้external/perfetto/tools/record_android_trace -o bitmaps.perfetto-trace \ -t 15s -b 64mb view gfx dalvik am res memory -a com.android.bitmaplabใน BitmapLab ให้แตะปุ่มจัดสรรและล้างซ้ำๆ
นอกจากนี้ ให้แตะรวม/แยกบิตแมปด้วย
วิเคราะห์การติดตามใน ui.perfetto.dev
ในส่วนกระบวนการสำหรับ com.android.bitmaplab คุณจะเห็นข้อมูลต่อไปนี้
* จำนวนบิตแมป: ตัวนับที่แสดงจำนวนบิตแมปที่ใช้งานอยู่
* หน่วยความจำบิตแมป: ตัวนับที่แสดงไบต์ทั้งหมดที่บิตแมปใช้
Slice ระดับสูง (Perfetto SDK)
BitmapLab ยังใช้ Perfetto SDK เพื่อปล่อย Slice ระดับสูงสำหรับการดำเนินการบิตแมปด้วย
ค้นหา BitmapLab_ ในการติดตามเพื่อค้นหาข้อมูลต่อไปนี้
* BitmapLab_parcelUnparcel: ส่วนที่ครอบคลุมตรรกะการแบ่งและการยกเลิกการแบ่ง
* BitmapLab_postNotification: Slices ที่ครอบคลุมขั้นตอนการโพสต์การแจ้งเตือน
ติดตามโฟลว์การแจ้งเตือน
เมื่อแตะโพสต์การแจ้งเตือน แอปจะสร้างการแจ้งเตือนที่มีบิตแมปปัจจุบันและส่งไปยังระบบ โค้ดเฟรมเวิร์กที่รับผิดชอบ สำหรับเรื่องนี้จะปล่อย Perfetto Slice พร้อมเหตุการณ์โฟลว์ที่เชื่อมต่อการจัดกลุ่ม (เขียนบิตแมปลงใน Parcel เพื่อส่งผ่าน Binder IPC) และการยกเลิกการจัดกลุ่ม (อ่านบิตแมปจาก Parcel ที่ฝั่งรับ)
ในภาพหน้าจอด้านล่าง คุณจะเห็นแอปที่แยกบิตแมปขนาดใหญ่ออกเป็นส่วนๆ เพื่อ
ใช้ในธุรกรรม Binder เพื่อโพสต์การแจ้งเตือน และการแยกส่วนที่เกี่ยวข้องในกระบวนการ system_server

การใช้ Perfetto ช่วยให้คุณติดตามบิตแมปการแจ้งเตือนเดียวกันได้เมื่อมีการเผยแพร่เพิ่มเติมในเธรดและกระบวนการต่างๆ เช่น จากเธรด Binder ใน system_server (ซึ่งใช้เซิร์ฟเวอร์ Binder INotificationManager) ไปยังเธรด Worker ของ system_server ซึ่งอาจส่งต่อบิตแมปเดียวกันไปยัง com.android.systemui เพื่อแสดงในการแจ้งเตือน
ความท้าทายเกี่ยวกับแอปของระบบ
แอประบบ เช่น SystemUI (การแจ้งเตือน) และ Launcher มีความท้าทายเฉพาะตัว
- เนื้อหาที่ไม่จำกัด: การแจ้งเตือนและวิดเจ็ตอาจมีจำนวนมาก หากแต่ละ รายการมีบิตแมปขนาดใหญ่ ระบบอาจใช้หน่วยความจำจนหมดอย่างรวดเร็ว
- การทำซ้ำ: ไอคอนแอปเดียวกันอาจอยู่ในแคชของ Launcher พื้นที่การแจ้งเตือนของ SystemUI และแอปการตั้งค่า
- การแชร์ผ่านบัฟเฟอร์ฮาร์ดแวร์: เพื่อลดปัญหานี้ คอมโพเนนต์ของระบบ
กำลังเปลี่ยนไปใช้บริการ "การออฟโหลดรูปภาพ" แบบรวมศูนย์ที่แชร์อินสแตนซ์
HardwareBufferในกระบวนการต่างๆ การระบุแหล่งที่มาของ DMABuf: บิตแมปฮาร์ดแวร์จะช่วยประหยัดพื้นที่ฮีป แต่ใช้หน่วยความจำ DMABuf ซึ่งระบุแหล่งที่มาของกระบวนการที่เฉพาะเจาะจงในเครื่องมือหน่วยความจำมาตรฐาน ได้ยากกว่า
ใช้
adb shell dmabuf_dumpเพื่อดูการจัดสรร DMABuf ทั่วทั้งระบบ เครื่องมือนี้ จะแสดงรายละเอียดบัฟเฟอร์ต่อกระบวนการดังนี้droid.bitmaplab:19562 Name Rss Pss nr_procs Inode Exporter <unknown> 3840 kB 1280 kB 3 3397 virtio_gpu system 12 kB 4 kB 3 3398 system <unknown> 3840 kB 1920 kB 2 3399 virtio_gpu system 12 kB 6 kB 2 3400 system PROCESS TOTAL 11556 kB 5136 kB- RSS: ขนาดรวมของบัฟเฟอร์หากมีการแมปในกระบวนการ
- Pss: ขนาดตามสัดส่วน (RSS หารด้วยจำนวนกระบวนการ ที่ใช้บัฟเฟอร์ร่วมกัน) นี่คือเมตริกที่ดีที่สุดสำหรับการบัญชี
- nr_procs: จำนวนกระบวนการที่อ้างอิงถึงบัฟเฟอร์นี้ในปัจจุบัน
- Exporter: ไดรเวอร์ที่จัดสรรบัฟเฟอร์ (เช่น
virtio_gpuใน Cuttlefish หรือฮีป Ion/DMA-BUF เฉพาะผู้จำหน่ายในฮาร์ดแวร์)
นอกจากนี้ คุณยังใช้
adb shell dmabuf_dump -bเพื่อดูสรุปบัฟเฟอร์ทั้งหมดและ การใช้งาน DMA-BUF ทั่วทั้งระบบได้ด้วย