บิตแมปและหน่วยความจำ

ออบเจ็กต์บิตแมปมักเป็นองค์ประกอบเดียวที่ใหญ่ที่สุดซึ่งมีส่วนทำให้แอปพลิเคชันใช้หน่วยความจำที่ใช้มาก ไม่ว่าจะเป็นไอคอนแอป รูปภาพการแจ้งเตือน หรือเนื้อหาสื่อ การจัดการบิตแมปที่ไม่มีประสิทธิภาพอาจทำให้เกิดข้อผิดพลาดหน่วยความจำไม่พอ (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 มีการแสดงภาพที่ยอดเยี่ยมสำหรับบิตแมป

  1. ใน BitmapLab ให้จัดสรรบิตแมป 2-3 รายการ
  2. บันทึกฮีปดัมป์ด้วยแฟล็ก -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
    
  3. เปิด localhost:7100 แล้วมองหาลิงก์ Bitmaps ในแถบด้านข้างหรือ ค้นหาคลาส Bitmap

  4. AHAT จะแสดงผลบิตแมปในเบราว์เซอร์จริงๆ ทำให้ระบุได้ง่ายว่ารูปภาพใดใช้หน่วยความจำมากเกินไป

AHAT แสดงบิตแมปที่เรนเดอร์

3. แทร็กบิตแมปใน Perfetto

Perfetto สามารถติดตามการจัดสรรบิตแมปและจำนวนได้เมื่อเวลาผ่านไป เคาน์เตอร์เหล่านี้จะ ปล่อยออกมาโดยเฟรมเวิร์ก Android เมื่อเปิดใช้หมวดหมู่ gfx ใน atrace สำหรับ แอปพลิเคชันที่เฉพาะเจาะจง

  1. เริ่มการติดตาม คุณต้องระบุ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
    
  2. ใน BitmapLab ให้แตะปุ่มจัดสรรและล้างซ้ำๆ

  3. นอกจากนี้ ให้แตะรวม/แยกบิตแมปด้วย

  4. วิเคราะห์การติดตามใน 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 แสดงโฟลว์จาก BitmapLab ไปยัง system_server ผ่านการแจ้งเตือน

การใช้ Perfetto ช่วยให้คุณติดตามบิตแมปการแจ้งเตือนเดียวกันได้เมื่อมีการเผยแพร่เพิ่มเติมในเธรดและกระบวนการต่างๆ เช่น จากเธรด Binder ใน system_server (ซึ่งใช้เซิร์ฟเวอร์ Binder INotificationManager) ไปยังเธรด Worker ของ system_server ซึ่งอาจส่งต่อบิตแมปเดียวกันไปยัง com.android.systemui เพื่อแสดงในการแจ้งเตือน

ความท้าทายเกี่ยวกับแอปของระบบ

แอประบบ เช่น SystemUI (การแจ้งเตือน) และ Launcher มีความท้าทายเฉพาะตัว

  1. เนื้อหาที่ไม่จำกัด: การแจ้งเตือนและวิดเจ็ตอาจมีจำนวนมาก หากแต่ละ รายการมีบิตแมปขนาดใหญ่ ระบบอาจใช้หน่วยความจำจนหมดอย่างรวดเร็ว
  2. การทำซ้ำ: ไอคอนแอปเดียวกันอาจอยู่ในแคชของ Launcher พื้นที่การแจ้งเตือนของ SystemUI และแอปการตั้งค่า
  3. การแชร์ผ่านบัฟเฟอร์ฮาร์ดแวร์: เพื่อลดปัญหานี้ คอมโพเนนต์ของระบบ กำลังเปลี่ยนไปใช้บริการ "การออฟโหลดรูปภาพ" แบบรวมศูนย์ที่แชร์อินสแตนซ์ HardwareBuffer ในกระบวนการต่างๆ
  4. การระบุแหล่งที่มาของ 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 ทั่วทั้งระบบได้ด้วย


← Java | ↑ ขึ้น | เนทีฟ →