เครื่องเก็บขยะใน Android


108

ฉันได้เห็นคำตอบของ Android มากมายที่แนะนำให้โทรหาคนเก็บขยะในบางสถานการณ์

เป็นแนวทางปฏิบัติที่ดีในการขอตัวเก็บขยะใน Android ก่อนดำเนินการด้วยความจำเสื่อมหรือไม่? ถ้าไม่ฉันควรโทรหาก็ต่อเมื่อได้รับOutOfMemoryข้อผิดพลาด?

มีสิ่งอื่นใดบ้างที่ฉันควรใช้ก่อนหันไปหาคนเก็บขยะ?

คำตอบ:


144

สำหรับรุ่นก่อน3.0 รังผึ้ง : ใช่ทำโทร System.gc()

ฉันพยายามสร้าง Bitmaps แต่ได้รับ "VM out of memory error" อยู่เสมอ แต่เมื่อฉันโทรไปSystem.gc()ครั้งแรกมันก็โอเค

เมื่อสร้างบิตแมป Android มักจะล้มเหลวโดยมีข้อผิดพลาดหน่วยความจำไม่เพียงพอและไม่พยายามรวบรวมขยะก่อน ดังนั้นโทรSystem.gc()และคุณมีหน่วยความจำเพียงพอที่จะสร้าง Bitmaps

หากต้องการสร้าง Objects ฉันคิดว่าSystem.gcจะถูกเรียกโดยอัตโนมัติหากจำเป็น แต่ไม่ใช่สำหรับการสร้างบิตแมป มันล้มเหลว

ดังนั้นฉันขอแนะนำให้โทรด้วยตนเองSystem.gc()ก่อนสร้างบิตแมป


39
เนื่องจาก pre-honeycomb ข้อมูลบิตแมปจะไม่ถูกเก็บไว้ใน VM ดังนั้นจึงไม่เรียกใช้ GC ไม่ควรเป็นปัญหาใน Honeycomb และในภายหลัง
Timmmm

2
@ Timmmm แต่จริงๆแล้วมันเป็นปัญหาของฉันฉันแค่ตั้งค่า largeheap เป็น true
Lei Leyba

118

พูดโดยทั่วไปในการปรากฏตัวของการเก็บขยะที่มันเป็นไม่เคยปฏิบัติที่ดีที่จะเรียก GC ด้วยตนเอง GC ถูกจัดระเบียบโดยใช้อัลกอริทึมฮิวริสติกซึ่งทำงานได้ดีที่สุดเมื่อปล่อยไว้ในอุปกรณ์ของตนเอง การเรียก GC ด้วยตนเองมักจะทำให้ประสิทธิภาพลดลง

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


8
+1 สำหรับคำตอบที่ไม่ขึ้นกับแพลตฟอร์ม รอดูว่ามีใครตอบคำถามเฉพาะ Android หรือไม่ก่อนที่จะเลือก
hpique

8
ฉันเห็นด้วยกับสิ่งที่คุณเขียนหากคุณยอมให้ "ประสิทธิภาพ" หมายถึงสิ่งที่ทั่วไปมากกว่าประสิทธิภาพของ CPU บนอุปกรณ์ Android การรับรู้ประสิทธิภาพของผู้ใช้เป็นสิ่งสำคัญยิ่ง นักพัฒนาอาจต้องการเรียกใช้ GC ในขณะที่ผู้ใช้กดปุ่มเป็นต้นดังนั้นผู้ใช้จะไม่ทราบถึง GC
ประธาน James K. Polk

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

4
Pre-honeycomb ข้อมูลบิตแมปจะไม่ถูกเก็บไว้ในหน่วยความจำ VM ระบบจะตรวจไม่พบเมื่อคุณใช้หน่วยความจำที่ไม่ใช่ VM มากเกินไปเนื่องจากบิตแมปและเรียกใช้ GC (ซึ่งจะเรียกใช้Bitmap.finalize()วิธีการที่ทำให้หน่วยความจำที่ไม่ใช่ VM ว่าง) ดังนั้นในกรณีนี้คุณควรข้ามส่วนหัวของ GC แล้ววิ่งBitmap.recycle()หรือSystem.gc()ตามความเหมาะสม แต่ก่อนรังผึ้งเท่านั้น
Timmmm

1
@ThomasPornin - ในทางกลับกันในฐานะโปรแกรมเมอร์แอปพลิเคชันคุณรู้บางสิ่งบางอย่างที่ระบบปฏิบัติการไม่ทราบ: บางครั้งที่แอปของคุณกำลังจะทำการเปลี่ยนแปลงครั้งใหญ่ในการใช้หน่วยความจำและจะเป็นการรบกวนน้อยลง ประสบการณ์ผู้ใช้ที่จะหยุดการทำงานชั่วคราวในขณะนี้กว่าในช่วงเวลาโดยพลการในอนาคต ซึ่งหมายความว่าอาจมีเหตุผลที่จะโทรไม่บ่อยนักในช่วงการเปลี่ยนภาพหลักในการใช้งานแอป สิ่งเหล่านี้หาได้ยากในแง่ที่ว่าใคร ๆ ก็ไม่ควรโทรหา GC บ่อยๆ แต่เป็นเรื่องธรรมดาที่แอปสำคัญ ๆ เกือบทุกแอปจะมีช่วงเวลาที่รู้จักกันโดยการออกแบบ
ToolmakerSteve

26

แอปพลิเคชั่น Android มีหน่วยความจำไม่เพียงพอหากเราจัดการบิตแมปไม่ถูกต้องวิธีแก้ปัญหาคือ

if(imageBitmap != null) {
    imageBitmap.recycle();
    imageBitmap = null;
}
System.gc();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;
imageBitmap = BitmapFactory.decodeFile(URI, options);
Bitmap  scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, 200, 200, true);
imageView.setImageBitmap(scaledBitmap);

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

หากยังคงประสบปัญหาคุณสามารถเพิ่มบรรทัดเหล่านี้ได้เช่นกัน

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];
options.inPurgeable = true;

สำหรับข้อมูลเพิ่มเติมโปรดดูที่ลิงค์นี้

https://web.archive.org/web/20140514092802/http://voices.yahoo.com/android-virtual-machine-vm-out-memory-error-7342266.html?cat=59


หมายเหตุ: เนื่องจาก "หยุดชั่วคราว" ที่เกิดจากการดำเนินการ gc จึงไม่แนะนำให้ทำก่อนการจัดสรรบิตแมปแต่ละครั้ง

การออกแบบที่เหมาะสมคือ:

  1. ฟรีบิตแมปทั้งหมดที่ไม่ต้องการอีกต่อไปตามif / recycle / nullรหัสที่แสดง (สร้างวิธีการเพื่อช่วยในเรื่องนั้น)

  2. System.gc();

  3. จัดสรรบิตแมปใหม่


19

หากคุณได้รับ OutOfMemoryError ก็มักจะสายเกินไปที่จะโทรหาคนเก็บขยะ ...

นี่คือคำพูดจากนักพัฒนา Android:

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

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


6
คำพูดนั้นตีความไปทางอื่นไม่ได้หรือ? บางทีอาจต้องเรียก GC ด้วยตนเองเพื่อรวบรวมวัตถุเหล่านั้นก่อนที่จะใช้หน่วยความจำมากเกินไป
hpique

@hgpc: ฉันไม่เห็นว่าใบเสนอราคาสามารถตีความในแบบที่คุณแนะนำได้อย่างไร ฉันเดาว่าเอกสารเป็นคำสารภาพยอมรับว่า GC ของพวกเขานั้นเรียบง่าย เมื่อหน่วยความจำเหลือน้อย GC เต็มจะดำเนินการ
ประธาน James K. Polk

การใช้อ็อบเจ็กต์และเฟรมเวิร์กที่ซับซ้อนอย่างเต็มที่โดยใช้อ็อบเจ็กต์ที่ซับซ้อนมีแนวโน้มที่จะใช้เวลาในการพัฒนาได้ดีกว่าการเพิ่มประสิทธิภาพก่อนกำหนด การกังวลเกี่ยวกับการเพิ่มประสิทธิภาพเป็นกับดักที่ง่ายกว่าสำหรับ Android มากกว่า Enterprise Java เนื่องจากเรามักจะคิดถึงประสิทธิภาพการทำงานในขณะที่เข้ารหัสแอปบนอุปกรณ์เคลื่อนที่ โดยเฉพาะอย่างยิ่งในฐานะผู้พัฒนาเฟรมเวิร์กที่ต้องคิดถึงประสิทธิภาพการทำงานทำให้มุมมองนั้นรั่วไหลลงในเอกสารอย่างเป็นทางการเมื่อพวกเขาไม่ระมัดระวัง
LateralFractal

9

ดูเหมือนว่าSystem.gc()จะไม่ทำงานบน Art Android 6.0.1 Nexus 5x ดังนั้นฉันจึงใช้Runtime.getRuntime().gc();แทน


1
System.gc()Runtime.getRuntime().gc()เป็นฟังก์ชั่นสำหรับกระดาษห่อ ดูandroid.googlesource.com/platform/libcore/+/…
นาธานเอฟ

ใช่จากแหล่งที่มาและเอกสารดูเหมือนจะเหมือนกัน แต่System.gc()ไม่ได้ผลสำหรับฉัน แต่Runtime.getRuntime().gc()ทำ!
Ivan

7

แอพของฉันจัดการรูปภาพจำนวนมากและมันก็ตายด้วย OutOfMemoryError สิ่งนี้ช่วยฉันได้ ใน Manifest.xml เพิ่ม

<application
....
   android:largeHeap="true"> 

9
Google ไม่ชอบสิ่งนี้
Pedro Paulo Amorim

1
@PedroPauloAmorim อธิบายว่าทำไม?
Machado

2
อย่าใช้ largeHeap เว้นแต่คุณจะแน่ใจว่าคุณทำอะไรอยู่ การใช้ฮีปขนาดใหญ่ทำให้ตัวเก็บขยะทำงานหนักขึ้นเนื่องจากต้องวนข้อมูลขยะจำนวนมากก่อนที่จะล้างหน่วยความจำ
Prakash

7

โดยทั่วไปคุณไม่ควรเรียก GC อย่างชัดเจนด้วย System.gc () มีแม้แต่การบรรยาย IO ( http://www.youtube.com/watch?v=_CruQY55HOk ) ที่พวกเขาอธิบายว่า GC หยุดค่าเฉลี่ยการบันทึกเป็นอย่างไรและพวกเขายังระบุว่าจะไม่เรียก System.gc () เพราะ Dalvik รู้ดีกว่า มากกว่าคุณเมื่อทำเช่นนั้น

ในทางกลับกันตามที่กล่าวไว้ในคำตอบข้างต้นกระบวนการ GC ใน Android (เช่นเดียวกับทุกอย่าง) บางครั้งก็มีปัญหา ซึ่งหมายความว่าอัลกอริทึม Dalvik GC ไม่เหมือนกับ Hotspot หรือ JRockit JVM และอาจเกิดข้อผิดพลาดในบางครั้ง หนึ่งในโอกาสนั้นคือเมื่อจัดสรรวัตถุบิตแมป นี่เป็นสิ่งที่ยุ่งยากเพราะใช้หน่วยความจำ Heap และ Non Heap และเนื่องจากบิตแมปที่หลวม ๆ หนึ่งชิ้นบนอุปกรณ์ที่ จำกัด หน่วยความจำก็เพียงพอที่จะให้ข้อยกเว้น OutOfMemory แก่คุณ ดังนั้นการเรียกใช้หลังจากที่คุณไม่ต้องการบิตแมปนี้อีกต่อไปโดยทั่วไปแล้วนักพัฒนาหลายคนจะแนะนำและถือเป็นแนวทางปฏิบัติที่ดีสำหรับบางคน

แนวทางปฏิบัติที่ดีกว่าคือการใช้. recycle () บนบิตแมปเนื่องจากเป็นสิ่งที่วิธีนี้ทำขึ้นเพื่อทำเครื่องหมายหน่วยความจำดั้งเดิมของบิตแมปว่าปลอดภัยที่จะลบ โปรดทราบว่านี่ขึ้นอยู่กับเวอร์ชันมากซึ่งหมายความว่าโดยทั่วไปแล้วจะต้องใช้กับ Android เวอร์ชันเก่ากว่า (ฉันคิดว่าก่อน 3.0) แต่จะไม่จำเป็นต้องใช้ในเวอร์ชันที่ใหม่กว่า นอกจากนี้มันจะไม่เจ็บมากเมื่อใช้กับอีเธอร์เวอร์ชันใหม่ ๆ (อย่าทำแบบนี้วนซ้ำหรืออะไรทำนองนั้น) รันไทม์ ART ใหม่เปลี่ยนไปมากที่นี่เพราะพวกเขาแนะนำ Heap "พาร์ติชัน" พิเศษสำหรับวัตถุขนาดใหญ่ แต่ฉันคิดว่าการทำเช่นนี้กับ ART ether จะไม่เจ็บมากนัก

นอกจากนี้ข้อควรทราบที่สำคัญอย่างหนึ่งเกี่ยวกับ System.gc () วิธีนี้ไม่ใช่คำสั่งที่ Dalvik (หรือ JVM) มีหน้าที่ต้องตอบสนอง ลองพิจารณาเพิ่มเติมเช่นพูดกับ Virtual machine "คุณช่วยเก็บขยะได้ไหมถ้ามันไม่ยุ่งยาก"



3

ฉันจะบอกว่าไม่เพราะเอกสารของนักพัฒนาเกี่ยวกับสถานะการใช้ RAM :

...
GC_EXPLICIT

GC ที่ชัดเจนเช่นเมื่อคุณโทรหาgc () (ซึ่งคุณควรหลีกเลี่ยงการโทรและวางใจให้ GC ทำงานเมื่อจำเป็น)

...

ฉันได้เน้นส่วนที่เกี่ยวข้องเป็นตัวหนา

ดูซีรีส์ YouTube รูปแบบการทำงานของ Androidซึ่งจะแสดงเคล็ดลับในการจัดการการใช้หน่วยความจำของแอปของคุณ (เช่นใช้ArrayMaps and s ของ Android SparseArrayแทนHashMaps)



2

ไม่จำเป็นต้องโทรหาคนเก็บขยะหลังจากOutOfMemoryErrorไฟล์.

Javadoc ระบุไว้อย่างชัดเจน:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

ดังนั้นคนเก็บขยะจึงพยายามเพิ่มหน่วยความจำก่อนที่จะสร้างข้อผิดพลาด แต่ไม่สำเร็จ


8
Javadoc ของ Android ไม่ได้ระบุไว้และมีแนวโน้มว่าการใช้งานจะแตกต่างกัน d.android.com/reference/java/lang/OutOfMemoryError.html
hpique

คุณถูกต้องว่าสิ่งนี้ใช้ไม่ได้กับ Android แต่อีกครั้งคุณไม่สามารถจัดการ OOE ที่ runtime ether ได้ดังนั้นคุณจึงไม่สามารถทำอะไรได้มากนักแม้ว่าจะเป็น / ไม่จริงก็ตาม
Igor Čordaš
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.