คุณจะตรวจสอบขนาดแอพพลิเคชั่นฮีปที่พร้อมใช้งานสำหรับแอพ Android โดยทางโปรแกรมได้อย่างไร
ฉันได้ยินมาว่ามีฟังก์ชั่นที่ทำสิ่งนี้ใน SDK รุ่นที่ใหม่กว่า ไม่ว่าในกรณีใดฉันกำลังมองหาโซลูชันที่ใช้งานได้ตั้งแต่ 1.5 ขึ้นไป
คุณจะตรวจสอบขนาดแอพพลิเคชั่นฮีปที่พร้อมใช้งานสำหรับแอพ Android โดยทางโปรแกรมได้อย่างไร
ฉันได้ยินมาว่ามีฟังก์ชั่นที่ทำสิ่งนี้ใน SDK รุ่นที่ใหม่กว่า ไม่ว่าในกรณีใดฉันกำลังมองหาโซลูชันที่ใช้งานได้ตั้งแต่ 1.5 ขึ้นไป
คำตอบ:
มีสองวิธีที่จะคิดเกี่ยวกับวลีของคุณ "ขนาดฮีพของแอปพลิเคชันที่มี":
แอพของฉันสามารถใช้ heap ได้มากเท่าไหร่ก่อนที่จะมีข้อผิดพลาดเกิดขึ้น? และ
แอพของฉันควรใช้heap เท่าไรตามข้อ จำกัด ของเวอร์ชั่น Android OS และฮาร์ดแวร์ของอุปกรณ์ของผู้ใช้
มีวิธีที่แตกต่างกันสำหรับการพิจารณาแต่ละข้อด้านบน
สำหรับรายการ 1 ด้านบน: maxMemory()
ซึ่งสามารถเรียกใช้ (เช่นในonCreate()
วิธีการของกิจกรรมหลัก) ดังนี้:
Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));
วิธีการนี้จะบอกคุณว่าหลายรวมไบต์ของกองแอปของคุณได้รับอนุญาตในการใช้งาน
สำหรับรายการ 2 ด้านบน: getMemoryClass()
ซึ่งสามารถเรียกใช้ดังนี้:
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));
วิธีนี้บอกให้คุณทราบว่าควรใช้ฮีปจำนวนกี่เมกะไบต์หากต้องการเคารพขีด จำกัด ของอุปกรณ์ที่มีอยู่อย่างเหมาะสมและสิทธิ์ของแอพอื่น ๆ ที่จะทำงานโดยไม่ต้องถูกบังคับซ้ำ ๆ ในรอบ/ เนื่องจากมันหยาบคาย ล้างออกจากหน่วยความจำขณะที่แอปช้างของคุณอาบน้ำในอ่างจากุซซี่ AndroidonStop()
onResume()
ความแตกต่างนี้ไม่ได้บันทึกไว้อย่างชัดเจนเท่าที่ฉันรู้ แต่ฉันได้ทดสอบสมมติฐานนี้ในอุปกรณ์ Android ห้ารุ่น (ดูด้านล่าง) และยืนยันความพึงพอใจของฉันเองว่านี่เป็นการตีความที่ถูกต้อง
สำหรับ Android เวอร์ชันสต็อกmaxMemory()
โดยทั่วไปแล้วจะส่งคืนจำนวนเมกะไบต์เท่ากันตามที่ระบุในgetMemoryClass()
(เช่นประมาณหนึ่งล้านเท่าของค่าหลัง)
สถานการณ์เดียว (ซึ่งฉันรู้) ซึ่งทั้งสองวิธีสามารถแยกความแตกต่างได้บนอุปกรณ์ที่รูทซึ่งใช้ Android เวอร์ชันเช่น CyanogenMod ซึ่งอนุญาตให้ผู้ใช้เลือกขนาดฮีปที่มีขนาดใหญ่สำหรับแต่ละแอป ใน CM ตัวอย่างตัวเลือกนี้จะปรากฏภายใต้ "การตั้งค่า CyanogenMod" / "ประสิทธิภาพ" / "ขนาดฮีพ VM"
หมายเหตุ: โปรดทราบว่าการตั้งค่านี้ด้วยตนเองสามารถส่งข้อความถึงระบบของคุณโดยเฉพาะถ้าคุณเลือกค่าที่น้อยกว่าปกติสำหรับอุปกรณ์ของคุณ
นี่คือผลการทดสอบของฉันที่แสดงค่าที่ส่งคืนโดยmaxMemory()
และgetMemoryClass()
สำหรับอุปกรณ์ที่ต่างกันสี่เครื่องที่ใช้ CyanogenMod โดยใช้ค่าฮีปที่แตกต่างกันสองค่า (ตั้งค่าด้วยตนเอง) สำหรับแต่ละค่า:
นอกเหนือจากข้างต้นฉันทดสอบบนแท็บเล็ต Novo7 Paladin ที่ใช้ Ice Cream Sandwich นี่เป็นรุ่นพื้นฐานของ ICS ยกเว้นว่าฉันได้รูทแท็บเล็ตผ่านกระบวนการง่าย ๆ ที่ไม่ได้แทนที่ทั้งระบบปฏิบัติการและโดยเฉพาะอย่างยิ่งไม่ได้มีส่วนต่อประสานที่จะอนุญาตให้ปรับขนาดฮีปด้วยตนเอง
สำหรับอุปกรณ์นั้นผลลัพธ์ที่ได้คือ:
นอกจากนี้ (ต่อ Kishore ในความคิดเห็นด้านล่าง):
และ (ต่อความคิดเห็นของ akauppi):
ตามความคิดเห็นจาก cmcromance:
และ (ต่อความคิดเห็นขององค์):
อุปกรณ์อื่น ๆ
ฉันยังไม่ได้ทดสอบทั้งสองวิธีโดยใช้ Android พิเศษ: largeHeap = "true" ตัวเลือกรายการที่มีให้ตั้งแต่ Honeycomb แต่ด้วย cmcromance และ tencent เรามีค่าขนาดใหญ่ตัวอย่างบางอย่างตามที่รายงานข้างต้น
ฉันคาดหวัง (ซึ่งดูเหมือนว่าจะได้รับการสนับสนุนโดยหมายเลข largeHeap ด้านบน) จะเป็นที่ตัวเลือกนี้จะมีผลที่คล้ายกันในการตั้งกองด้วยตนเองผ่านทางระบบปฏิบัติการที่ฝังราก - คือมันจะเพิ่มมูลค่าของmaxMemory()
ในขณะที่ออกgetMemoryClass()
เพียงอย่างเดียว มีวิธีอื่นคือ getLargeMemoryClass () ที่ระบุจำนวนหน่วยความจำที่อนุญาตสำหรับแอปที่ใช้การตั้งค่า largeHeap เอกสารสำหรับสถานะ getLargeMemoryClass () "แอปพลิเคชันส่วนใหญ่ไม่จำเป็นต้องใช้หน่วยความจำจำนวนนี้และควรจะอยู่กับขีด จำกัด getMemoryClass () แทน"
หากฉันเดาถูกต้องการใช้ตัวเลือกนั้นจะมีประโยชน์เช่นเดียวกับ (และภัยพิบัติ) เช่นเดียวกับการใช้พื้นที่ที่มีให้โดยผู้ใช้ที่ได้เพิ่มจำนวน heap ผ่านทางระบบปฏิบัติการที่รูต (เช่นหากแอปของคุณใช้หน่วยความจำเพิ่มเติม มันอาจจะไม่เล่นได้ดีเหมือนแอพอื่น ๆ ที่ผู้ใช้กำลังทำงานในเวลาเดียวกัน)
โปรดจำไว้ว่าคลาสหน่วยความจำไม่จำเป็นต้องมีหลายเท่าของ 8MB
เราสามารถเห็นได้จากด้านบนว่าgetMemoryClass()
ผลลัพธ์ไม่เปลี่ยนแปลงสำหรับการกำหนดค่าอุปกรณ์ / OS ที่กำหนดในขณะที่ค่า maxMemory () จะเปลี่ยนไปเมื่อฮีปถูกตั้งค่าต่างกันโดยผู้ใช้
ประสบการณ์จริงของฉันคือบน G1 (ซึ่งมีคลาสหน่วยความจำ 16) ถ้าฉันเลือก 24 เมกะไบต์เป็นขนาดฮีปด้วยตนเองฉันสามารถทำงานได้โดยไม่ผิดพลาดแม้เมื่อการใช้หน่วยความจำของฉันได้รับอนุญาตให้ลอยขึ้นสู่ 20MB (อาจเป็นได้ สูงถึง 24MB แม้ว่าฉันจะไม่ได้ลอง) แต่แอพขนาดใหญ่อื่น ๆ ที่คล้ายกันอาจได้รับการลบออกจากหน่วยความจำอันเป็นผลมาจากความแอพของฉันเอง และในทางกลับกันแอพของฉันอาจถูกลบออกจากหน่วยความจำหากแอปที่มีการบำรุงรักษาสูงอื่น ๆ เหล่านี้ถูกนำไปสู่เบื้องหน้าโดยผู้ใช้
ดังนั้นคุณไม่สามารถข้ามจำนวนหน่วยความจำที่ระบุmaxMemory()
ได้ และคุณควรพยายามgetMemoryClass()
ที่จะอยู่ภายในขอบเขตที่กำหนดโดย วิธีหนึ่งในการทำเช่นนั้นหากสิ่งอื่นล้มเหลวอาจเป็นการ จำกัด การทำงานของอุปกรณ์ดังกล่าวในลักษณะที่สงวนหน่วยความจำไว้
ในที่สุดหากคุณวางแผนที่จะใช้จำนวนเมกะไบต์ที่ระบุไว้ในgetMemoryClass()
คำแนะนำของฉันจะทำงานอย่างหนักและยาวนานในการบันทึกและเรียกคืนสถานะแอปของคุณเพื่อให้ประสบการณ์การใช้งานของผู้ใช้ไม่สะดุดหากเกิดวงจรonStop()
/ onResume()
เกิดขึ้น
ในกรณีของฉันด้วยเหตุผลด้านประสิทธิภาพฉัน จำกัด แอพของฉันไปยังอุปกรณ์ที่ใช้ 2.2 และสูงกว่าและนั่นหมายความว่าอุปกรณ์เกือบทั้งหมดที่ใช้แอพของฉันจะมีหน่วยความจำระดับ 24 ขึ้นไป ดังนั้นฉันจึงสามารถออกแบบให้ใช้งานฮีปมากถึง 20MB และรู้สึกมั่นใจว่าแอพของฉันจะเล่นได้ดีกับแอพอื่น ๆ ที่ผู้ใช้อาจทำงานในเวลาเดียวกัน
แต่จะมีผู้ใช้ที่รูทไม่กี่คนที่โหลด Android เวอร์ชัน 2.2 ขึ้นไปไปยังอุปกรณ์รุ่นเก่า (เช่น G1) เมื่อคุณพบการกำหนดค่าเช่นนั้นคุณควรจะลดการใช้หน่วยความจำแม้ว่าmaxMemory()
จะบอกคุณว่าคุณสามารถสูงกว่า 16MB ที่getMemoryClass()
บอกคุณว่าคุณควรกำหนดเป้าหมาย และหากคุณไม่สามารถมั่นใจได้ว่าแอปของคุณจะอยู่ในงบประมาณนั้นอย่างน้อยก็ต้องแน่ใจว่าonStop()
/ onResume()
ทำงานได้อย่างราบรื่น
getMemoryClass()
ตามที่ระบุโดย Diane Hackborn (hackbod) ด้านบนจะสามารถใช้งานได้กับ API ระดับ 5 (Android 2.0) เท่านั้นและเมื่อเธอแนะนำคุณสามารถสันนิษฐานได้ว่าฮาร์ดแวร์ทางกายภาพของอุปกรณ์ใด ๆ ที่ใช้ระบบปฏิบัติการรุ่นก่อนหน้านี้ได้รับการออกแบบ เพื่อรองรับแอพที่เหมาะสมที่สุดที่มีพื้นที่ฮีปไม่เกิน 16MB
โดยคมชัดmaxMemory()
ตามเอกสารที่มีอยู่ทั้งหมดทางกลับไปที่ระดับ API 1. maxMemory()
บนก่อน 2.0 รุ่นอาจจะกลับค่า 16MB แต่ฉันทำเห็นว่าใน CyanogenMod ของฉัน (มากในภายหลัง) รุ่นผู้ใช้ สามารถเลือกค่าฮีปได้ต่ำสุดที่ 12MB ซึ่งน่าจะส่งผลให้ขีด จำกัด ฮีพลดลงและดังนั้นฉันขอแนะนำให้คุณทดสอบmaxMemory()
ค่าต่อไปแม้กระทั่งสำหรับเวอร์ชันของระบบปฏิบัติการก่อนหน้า 2.0 คุณอาจต้องปฏิเสธที่จะเรียกใช้ในกรณีที่ไม่น่าเป็นไปได้ที่ค่านี้จะถูกตั้งค่าต่ำกว่า 16MB หากคุณจำเป็นต้องมีตัวmaxMemory()
บ่งชี้มากกว่าตัว
APIอย่างเป็นทางการคือ:
เรื่องนี้ได้รับการแนะนำใน 2.0 ที่มีอุปกรณ์หน่วยความจำขนาดใหญ่ปรากฏขึ้น คุณสามารถสันนิษฐานได้ว่าอุปกรณ์ที่ใช้ระบบปฏิบัติการเวอร์ชันก่อนหน้านี้ใช้คลาสหน่วยความจำดั้งเดิม (16)
Debug.getNativeHeapSize()
ฉันจะคิดทำ แม้ว่าจะมีตั้งแต่ 1.0 แม้ว่า
Debug
ระดับมีจำนวนมากวิธีการที่ดีสำหรับการติดตามการจัดสรรและความกังวลประสิทธิภาพอื่น ๆ Activity.onLowMemory()
นอกจากนี้ถ้าคุณต้องการที่จะตรวจสอบสถานการณ์ที่หน่วยความจำต่ำตรวจสอบ
นี่คือวิธีที่คุณทำ:
รับขนาดฮีพสูงสุดที่แอปสามารถใช้:
Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();
รับจำนวนฮีปที่แอปของคุณใช้ในปัจจุบัน:
long usedMemory=runtime.totalMemory() - runtime.freeMemory();
รับจำนวนฮีปที่แอปของคุณสามารถใช้งานได้ (หน่วยความจำที่มีอยู่):
long availableMemory=maxMemory-usedMemory;
และเพื่อจัดรูปแบบแต่ละอย่างคุณสามารถใช้:
String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize);
ส่งคืนขนาดฮีพสูงสุดเป็นไบต์:
Runtime.getRuntime().maxMemory()
ฉันใช้ ActivityManager.getMemoryClass () แต่ใน CyanogenMod 7 (ฉันไม่ได้ทดสอบที่อื่น) จะส่งคืนค่าผิดถ้าผู้ใช้กำหนดขนาดฮีปด้วยตนเอง
getMemoryClass
ดูเหมือนจะบอกเป็นนัยว่าจำนวนอาจไม่เท่ากับขนาดฮีปที่มีสำหรับ vm ของคุณและเนื่องจากเอกสารสำหรับgetNativeHeapSize
... เงียบขรึมฉันคิดว่าRuntime.getRuntime().maxMemory()
เป็นคำตอบที่ดีที่สุด
การดำเนินการบางอย่างรวดเร็วกว่า java heap space manager การดำเนินการล่าช้าบางครั้งสามารถเพิ่มพื้นที่หน่วยความจำได้ คุณสามารถใช้วิธีนี้เพื่อหลีกเลี่ยงข้อผิดพลาดขนาดฮีพ:
waitForGarbageCollector(new Runnable() {
@Override
public void run() {
// Your operations.
}
});
/**
* Measure used memory and give garbage collector time to free up some
* space.
*
* @param callback Callback operations to be done when memory is free.
*/
public static void waitForGarbageCollector(final Runnable callback) {
Runtime runtime;
long maxMemory;
long usedMemory;
double availableMemoryPercentage = 1.0;
final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
final int DELAY_TIME = 5 * 1000;
runtime =
Runtime.getRuntime();
maxMemory =
runtime.maxMemory();
usedMemory =
runtime.totalMemory() -
runtime.freeMemory();
availableMemoryPercentage =
1 -
(double) usedMemory /
maxMemory;
if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {
try {
Thread.sleep(DELAY_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitForGarbageCollector(
callback);
} else {
// Memory resources are availavle, go to next operation:
callback.run();
}
}
AvailableMemoryPercentage
เป็นไปตามสูตร: จำนวนหน่วยความจำของอุปกรณ์ว่างในขณะนี้ MIN_AVAILABLE_MEMORY_PERCENTAGE
เป็นพารามิเตอร์ที่กำหนดเองของคุณซึ่งเป็นเกณฑ์ที่คุณเริ่มรอให้ตัวรวบรวมขยะทำงานได้
Asus Nexus 7 (2013) 32Gig: getMemoryClass () = 192 maxMemory () = 201326592
ฉันทำผิดพลาดในการทำต้นแบบเกมของฉันบน Nexus 7 แล้วค้นพบว่าหน่วยความจำในแทบจะในทันทีบนแท็บเล็ตทั่วไป 4.04 ของภรรยาของฉัน (memoryclass 48, maxmemory 50331648)
ฉันจะต้องปรับโครงสร้างโครงการของฉันเพื่อโหลดทรัพยากรให้น้อยลงเมื่อฉันพบว่าหน่วยความจำระดับต่ำ
มีวิธีใน Java เพื่อดูขนาดกองปัจจุบันหรือไม่ (ฉันสามารถดูได้อย่างชัดเจนใน logCat เมื่อทำการดีบั๊ก แต่ฉันต้องการวิธีที่จะเห็นมันในรหัสเพื่อปรับตัวเช่นถ้า currentheap> (maxmemory / 2) ยกเลิกการโหลดบิตแมปคุณภาพสูงโหลดคุณภาพต่ำ
คุณหมายถึงโดยทางโปรแกรมหรือเพียงแค่ในขณะที่คุณกำลังพัฒนาและแก้ไขข้อบกพร่อง หากหลังคุณสามารถดูข้อมูลนั้นได้จากมุมมอง DDMS ใน Eclipse เมื่ออีมูเลเตอร์ของคุณ (อาจเป็นโทรศัพท์ทางกายภาพที่เสียบอยู่) กำลังรันอยู่มันจะแสดงรายการกระบวนการที่ทำงานอยู่ในหน้าต่างทางด้านซ้าย คุณสามารถเลือกได้และมีตัวเลือกในการติดตามการจัดสรรฮีป
Runtime rt = Runtime.getRuntime();
rt.maxMemory()
ค่าคือ b
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()
ค่าคือ MB
rt
ใด มีการประกาศที่ไหน?