Android: Bitmap recycle () ทำงานอย่างไร


90

สมมติว่าฉันโหลดภาพในวัตถุบิตแมปเช่น

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

ตอนนี้จะเกิดอะไรขึ้นถ้าฉันโหลดบิตแมปอื่นเช่น

myBitmap = BitmapFactory.decodeFile(myFile2);

เกิดอะไรขึ้นกับ myBitmap แรก ได้รับ Garbage Collected หรือไม่หรือฉันต้องรวบรวมขยะด้วยตนเองก่อนที่จะโหลดบิตแมปอื่นเช่น myBitmap.recycle()เหรอ?

นอกจากนี้ยังมีวิธีที่ดีกว่าในการโหลดภาพขนาดใหญ่และแสดงทีละภาพในขณะที่กำลังรีไซเคิลอยู่หรือไม่?

คำตอบ:


79

บิตแมปแรกไม่ใช่ขยะที่รวบรวมเมื่อคุณถอดรหัสบิตแมปที่สอง พนักงานเก็บขยะจะดำเนินการในภายหลังเมื่อใดก็ตามที่ตัดสินใจ หากคุณต้องการเพิ่มหน่วยความจำโดยเร็วคุณควรโทรrecycle()ก่อนที่จะถอดรหัสบิตแมปที่สอง

หากคุณต้องการโหลดภาพขนาดใหญ่จริงๆคุณควรสุ่มตัวอย่างใหม่ นี่คือตัวอย่าง: แปลกปัญหาหน่วยความจำไม่เพียงพอขณะโหลดภาพไปยังวัตถุบิตแมป


24

ฉันคิดว่าปัญหาคือ: ใน Android เวอร์ชันก่อน Honeycomb ข้อมูลดิบบิตแมปจริงจะไม่ถูกเก็บไว้ในหน่วยความจำ VM แต่อยู่ในหน่วยความจำเนทีฟแทน หน่วยความจำเนทีฟนี้จะถูกปลดปล่อยเมื่อBitmapวัตถุjava ที่เกี่ยวข้องคือ GC'd

อย่างไรก็ตามเมื่อคุณใช้หน่วยความจำเนทีฟหมดแล้ว dalvik GC จะไม่ถูกทริกเกอร์ดังนั้นจึงเป็นไปได้ว่าแอพของคุณใช้หน่วยความจำ java น้อยมากดังนั้น dalvik GC จะไม่ถูกเรียกใช้ แต่จะใช้หน่วยความจำเนทีฟจำนวนมากสำหรับบิตแมป ซึ่งทำให้เกิดข้อผิดพลาด OOM ในที่สุด

อย่างน้อยนั่นคือการคาดเดาของฉัน โชคดีใน Honeycomb และในภายหลังข้อมูลบิตแมปทั้งหมดจะถูกเก็บไว้ใน VM ดังนั้นคุณจึงไม่จำเป็นต้องใช้recycle()เลย แต่สำหรับผู้ใช้ 2.3 ล้านคน (การกระจายตัวของหมัดสั่น ) คุณควรใช้recycle()ทุกที่ที่เป็นไปได้ (ยุ่งยากมาก) หรือคุณอาจเรียกใช้ GC แทนก็ได้


21

คุณจะต้องโทรหา myBitmap.recycle () ก่อนที่จะโหลดภาพถัดไป

ขึ้นอยู่กับแหล่งที่มาของ myFile ของคุณ (เช่นหากเป็นสิ่งที่คุณไม่สามารถควบคุมขนาดต้นฉบับได้) เมื่อโหลดรูปภาพแทนที่จะเพียงแค่สุ่มตัวอย่างตัวเลขบางส่วนใหม่คุณควรปรับขนาดภาพให้เป็นขนาดที่แสดง

if (myBitmap != null) {
    myBitmap.recycle();
    myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

ฉันแคช displayWidth & displayHeight ในแบบคงที่ซึ่งฉันเริ่มต้นเมื่อเริ่มกิจกรรมของฉัน

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();

3
คุณไม่จำเป็นต้องเรียกว่า recycle () แต่เป็นความคิดที่ดีหากคุณต้องการเพิ่มหน่วยความจำทันที
Karu

13
คำตอบที่ได้รับการยอมรับระบุว่า "หากคุณต้องการเพิ่มหน่วยความจำโดยเร็วคุณควรเรียก recycle ()" คำตอบของคุณระบุว่า "คุณจะต้องโทรหา myBitmap.recycle ()" มีความแตกต่างระหว่าง "ควร" และ "จำเป็น" และข้อหลังไม่ถูกต้องในกรณีนี้
Karu

1
บริบทเป็นสิ่งสำคัญ คำถามคือ "นอกจากนี้ยังมีวิธีที่ดีกว่าในการโหลดภาพขนาดใหญ่และแสดงทีละภาพหลังจากการรีไซเคิลอีกครั้งในระหว่างทาง"
djunod

3
สำหรับ Android 4.1 ตัวอย่างข้างต้นอาจเสียหายเนื่องจาก createScaledBitmap สามารถส่งคืนอินสแตนซ์เดียวกันกับอินสแตนซ์เดิมในบางกรณี นั่นหมายความว่าคุณต้องตรวจสอบต้นฉบับนั้น! = myBitmap ก่อนที่จะรีไซเคิลต้นฉบับ
Jeremyfa

1
@Jeremyfa จะส่งคืนรูปภาพต้นฉบับก็ต่อเมื่อคุณระบุความกว้างและความสูงที่เหมือนกับต้นฉบับ ในกรณีนั้นการปรับสเกลคือการสงสัยดังนั้นจึงอาจช่วยประหยัดกระบวนการบางอย่างได้ด้วยการข้ามไปและส่งคืนรูปภาพต้นฉบับแทน มันไม่ควร "ทำลาย" อะไรแม้ว่า ...
Jabari

11

เมื่อบิตแมปถูกโหลดในหน่วยความจำอันที่จริงมันถูกสร้างขึ้นโดยข้อมูลสองส่วน ส่วนแรกรวมข้อมูลบางอย่างเกี่ยวกับบิตแมปอีกส่วนหนึ่งรวมถึงข้อมูลเกี่ยวกับพิกเซลของบิตแมป (ถูกสร้างขึ้นโดยอาร์เรย์ไบต์) ส่วนแรกออกในหน่วยความจำที่ใช้ Java ส่วนที่สองออกจากหน่วยความจำที่ใช้ C ++ สามารถใช้หน่วยความจำของกันและกันได้โดยตรง Bitmap.recycle () ใช้เพื่อเพิ่มหน่วยความจำของ C ++ หากคุณทำเช่นนั้น GC จะรวบรวมส่วนของ java และหน่วยความจำของ C จะถูกใช้เสมอ


+1 สำหรับวิธีอธิบายที่น่าสนใจ แต่ดีมากว่าทำไมหน่วยความจำไม่พร้อมใช้งานสำหรับ GC ในทันที - เป็นสิ่งที่ดี
Richard Le Mesurier

8

Timmmm พูดถูก

อ้างอิงจาก: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

นอกจากนี้ก่อนหน้า Android 3.0 (API ระดับ 11) ข้อมูลสำรองของบิตแมปจะถูกเก็บไว้ในหน่วยความจำดั้งเดิมซึ่งไม่ได้เผยแพร่ในลักษณะที่คาดเดาได้ซึ่งอาจทำให้แอปพลิเคชันใช้งานเกินขีด จำกัด หน่วยความจำชั่วครู่และเกิดปัญหา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.