คุณจะตรวจสอบขนาดแอพพลิเคชั่นฮีปที่พร้อมใช้งานสำหรับแอพ 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ใด มีการประกาศที่ไหน?