เกี่ยวกับการจัดการหน่วยความจำ

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

ความสำคัญของการเพิ่มประสิทธิภาพหน่วยความจำ

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

  • การป้องกันการเริ่มระบบใหม่ (ประสบการณ์ของผู้ใช้และการคงผู้ใช้ไว้): เมื่อผู้เล่นเปลี่ยนจากเกมของคุณไปทำอย่างอื่นชั่วคราว (เช่น ตอบการแจ้งเตือนหรือตรวจสอบข้อความ) ระบบปฏิบัติการจะย้ายกระบวนการของเกมไปไว้ในเบื้องหลัง หากหน่วยความจำที่ใช้ในเบื้องหลังของเกมสูงเกินไป Low Memory Killer (LMK) ของระบบจะจัดลำดับความสำคัญของการสิ้นสุดกระบวนการของเกมเพื่อเรียกคืน RAM สำหรับงานที่ทำงานอยู่เบื้องหน้า ครั้งถัดไปที่ผู้ใช้กลับมาเล่นเกมต่อ แทนที่จะเป็นการกลับมาเล่นต่อแบบอุ่นเครื่องที่ราบรื่นและรวดเร็ว เกมจะต้องผ่านการเริ่มใหม่แบบเย็นที่ใช้เวลานาน ซึ่งจะโหลดเนื้อหากราฟิก เสียง และไบนารีของเกมเอนจินที่มีขนาดใหญ่อีกครั้งทั้งหมดจากพื้นที่เก็บข้อมูล การรักษาการใช้งานหน่วยความจำในเบื้องหลังให้ต่ำจะช่วยป้องกันการสิ้นสุดการทำงานในเบื้องหลังโดยไม่แจ้งให้ทราบ ซึ่งจะช่วยรักษาสถานะของผู้ใช้และทำให้ผู้เล่นกลับมาเล่นต่อได้ทันที ดูรายละเอียดเพิ่มเติมเกี่ยวกับลักษณะการทำงานของ LMK ของระบบได้ที่คู่มือ Android Vitals - ตัวปิดแอปเมื่อหน่วยความจำเหลือน้อย
  • ความเสถียรของระบบนิเวศและอุปกรณ์: การใช้งานหน่วยความจำที่ไม่มีประสิทธิภาพและหน่วยความจำรั่วไหลจะทำให้ประสิทธิภาพโดยรวมของระบบลดลง เมื่อหน่วยความจำระบบเหลือน้อย ระบบจะเผชิญกับแรงกดดันอย่างรุนแรง ส่งผลให้อัตราเฟรมลดลง UI กระตุก และเสียงขัดข้อง หากแรงกดดันของหน่วยความจำรุนแรงเกินไป Low Memory Killer (LMK) ของระบบจะสิ้นสุดกระบวนการเบื้องหลังอย่างรุนแรง ซึ่งจะบังคับให้แอปพลิเคชันอื่นๆ ประสบปัญหาการเริ่มทำงานใหม่แบบเย็นที่ช้าและสถานะผู้ใช้สูญหายเมื่อผู้เล่นสลับระหว่างงาน
  • การสิ้นสุดการทำงานที่ระดับแพลตฟอร์ม: ตั้งแต่ Android 17 (ระดับ API 37) เป็นต้นไป ระบบจะดำเนินการเชิงรุกมากขึ้นในการสิ้นสุดกระบวนการที่ใช้หน่วยความจำมากเกินไป หากเกมใช้ทรัพยากรมากเกินไป ระบบปฏิบัติการอาจสิ้นสุดกระบวนการของเกมอย่างกะทันหันโดยไม่สร้างสแต็กเทรซมาตรฐาน
  • ความเข้ากันได้ของอุปกรณ์: แม้อุปกรณ์เรือธงจะมี RAM ขนาด 12 GB ถึง 16 GB แต่ผู้เล่นเกมทั่วโลกจำนวนมากใช้อุปกรณ์ที่มี RAM ขนาด 4 GB หรือ 6 GB การจัดการหน่วยความจำที่เหมาะสมช่วยให้มั่นใจได้ว่าเกมของคุณจะยังคงเข้าถึงได้และตอบสนองได้ดีในฮาร์ดแวร์ทุกระดับโดยไม่ต้องใช้แพ็กเกจเนื้อหาที่ซับซ้อนและแยกต่างหาก

ทำความเข้าใจหน่วยความจำใน Android

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

แนวคิดหลักเกี่ยวกับหน่วยความจำของ Android

ดูแนวคิดพื้นฐานเกี่ยวกับการจัดการหน่วยความจำระดับแพลตฟอร์มได้ในเอกสารประกอบภาพรวมการจัดการหน่วยความจำอย่างเป็นทางการ แหล่งข้อมูลนี้ครอบคลุมสถาปัตยกรรม 4 ด้าน ได้แก่

  • ภาพรวมของหน่วยความจำ: Android ใช้การแบ่งหน้าและการแมปหน่วยความจำ (mmap) เพื่อจัดการ RAM โดยไม่รองรับไฟล์สลับแบบดั้งเดิมในดิสก์ แต่จะอาศัยการบีบอัดหน้า (โดยใช้ zRAM) และการเรียกคืนหน้าเพื่อเพิ่มพื้นที่ว่างในหน่วยความจำจริง
  • การจัดสรรหน่วยความจำระหว่างกระบวนการต่างๆ: Android แชร์ RAM ไปทั่วทั้งระบบ โดยจะกำหนดฮีปที่เฉพาะเจาะจงสำหรับการเรียกใช้เครื่องเสมือน Dalvik หรือ ART ขณะเดียวกันก็อนุญาตให้สภาพแวดล้อมในการพัฒนาซอฟต์แวร์แบบเนทีฟ (เช่น เกมเอนจิน C++) ขอหน่วยความจำจากฮีประบบแบบเนทีฟได้
  • การจัดการหน่วยความจำของแอป: Android ทำงานภายใต้โมเดลแบบหลายกระบวนการ โดยคาดหวังให้แอปพลิเคชันตรวจสอบสถานะวงจรของตนเองแบบไดนามิกและปล่อยทรัพยากรที่ไม่จำเป็น (เช่น กราฟิกและบิตแมปที่ไม่ได้แคช) โดยสมัครใจเพื่อสนับสนุนประสิทธิภาพของระบบ
  • ภาพรวมของกระบวนการและชุดข้อความ: ระบบจะจัดหมวดหมู่กระบวนการเป็นลำดับชั้นตามระดับการมองเห็นและความสำคัญที่ผู้ใช้รับรู้ในปัจจุบัน โดยจะกำหนดว่ากระบวนการใดจะยังคงทำงานอยู่และกระบวนการใดจะสิ้นสุดก่อนในสภาวะที่หน่วยความจำเหลือน้อย

เมตริกหน่วยความจำที่ใช้ทั้งหมด

ตัวจำกัดหน่วยความจำ Android 17 ระดับแพลตฟอร์มจะประเมินการใช้กระบวนการโดยใช้หน่วยความจำที่ใช้ทั้งหมดแทนขนาด Resident ทั้งหมด (RSS) หรือขนาดหน่วยความจำเสมือน

หน่วยความจำที่ใช้ทั้งหมด = RSS แบบไม่ระบุตัวตน (RssAnon) + Swap ที่ไม่บีบอัด (VmSwap)

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

ข้อจำกัดด้านหน่วยความจำ

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

ตัวจำกัดหน่วยความจำใน Android 17 ขึ้นไป

Android 17 (ระดับ API 37) ขึ้นไปจะจัดการขีดจำกัดหน่วยความจำที่เข้มงวดต่อแอปโดยใช้ Linux cgroup v2 เพื่อป้องกันไม่ให้แอปแต่ละแอปทำให้ระบบไม่เสถียรทั้งระบบ ดูรายละเอียดเพิ่มเติมเกี่ยวกับการใช้งานทางเทคนิคได้ที่คู่มือตัวจำกัดหน่วยความจำของ AOSP และบล็อกการให้ความสำคัญกับประสิทธิภาพการใช้หน่วยความจำ: ขั้นตอนสำคัญสำหรับ Android 17

  • กลไก: ตัวจำกัดหน่วยความจำจะตรวจสอบกระบวนการทั้งหมดของแอปพลิเคชันและกำหนดขีดจำกัดแบบไดนามิกตามสถานะวงจรของกระบวนการ ดังนี้
    • กระบวนการที่มองเห็นได้ (เบื้องหน้า): กระบวนการของแอปที่กำลังแสดง UI คาดว่าจะเรียกใช้ชุดการทำงานของทรัพยากรที่ใหญ่ขึ้น และได้รับขีดจำกัดที่มากขึ้น
    • กระบวนการที่มองไม่เห็น (เบื้องหลังหรือบริการ): กระบวนการของแอปที่ทำงานอยู่โดยไม่แสดง UI จะถูกจำกัดให้ใช้โควต้าที่เข้มงวดและจำกัดมากขึ้น
  • แอตทริบิวต์เคอร์เนล: บริการนี้อาศัยแอตทริบิวต์หลัก 2 รายการ ได้แก่
    • memory.high: ขีดจำกัดแบบยืดหยุ่น เมื่อเกินขีดจำกัด เคอร์เนลจะควบคุมกระบวนการและพยายามเรียกคืนหน่วยความจำอย่างจริงจัง การเรียกคืนนี้อาจทำให้เกมมีประสิทธิภาพลดลง
    • memory.swap.max: จัดการขีดจำกัดสูงสุดของพื้นที่สลับหรือ zRAM ที่กระบวนการใช้ได้
  • ลักษณะการสิ้นสุดการทำงาน: หากกระบวนการยังคงจัดสรรหน่วยความจำแบบไม่ระบุชื่อเกิน memory.high และใช้ความจุสวอปจนหมด การจัดสรรจะล้มเหลว และระบบปฏิบัติการจะหยุดกระบวนการโดยไม่แจ้งให้ทราบ ระบบจะบันทึกการสิ้นสุดนี้โดยใช้ ApplicationExitInfo ภายใต้เหตุผลการออก Memory Limiter (พร้อมใช้งานตั้งแต่ Android 17, 26Q4 เป็นต้นไป)

ขีดจำกัดหน่วยความจำใหม่ใน Vitals ของ Play Console

Google Play ขอแนะนำเมตริกใหม่ใน Android Vitals ของ Play Console เพื่อช่วยให้นักพัฒนาแอประบุปัญหาเกี่ยวกับหน่วยความจำในเชิงรุกได้ Play Console จะติดตามหน่วยความจำที่ใช้ของ Anonymous RSS + Swap ในเปอร์เซ็นไทล์ที่ 90 (P90) ของเซสชันเกมเพื่อระบุค่าผิดปกติที่รุนแรง

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

ดูหลักเกณฑ์โดยละเอียดและขีดจำกัดหน่วยความจำได้ที่ Android Vitals - "เกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องคืออะไร"

บริการที่ผู้ใช้รับรู้

บริการที่รับรู้ได้คือกระบวนการเบื้องหลังที่สำคัญซึ่งระบบ Android ถือว่าผู้ใช้สังเกตเห็นได้ สถานะนี้ครอบคลุมกระบวนการทั้งหมดที่ทำงานอยู่ ดังนี้

  • บริการที่ทำงานอยู่เบื้องหน้า (FGS)
  • งานด่วน
  • งานการโอนข้อมูลที่เริ่มต้นโดยผู้ใช้
  • บริการที่มีผลผูกพันกับระบบหรือบริการที่มีผลผูกพันกับแอปพลิเคชันอื่นๆ

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

ข้อกำหนดของ R8

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

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

หากคุณสร้างด้วยเกมเอนจิน ลักษณะการทำงานเริ่มต้นของ R8 จะแตกต่างกันไปตามเอนจินและเวอร์ชัน ดังนี้

  • Unity: การลดขนาดโค้ดจะปิดอยู่โดยค่าเริ่มต้น คุณจึงต้องเปิดใช้ในการตั้งค่าโปรเจ็กต์ ดูข้อมูลเพิ่มเติมเกี่ยวกับการรองรับเวอร์ชันและขั้นตอนการกำหนดค่าได้ที่หัวข้อลดขนาดและเพิ่มประสิทธิภาพโค้ด Unity ด้วย R8
  • Unreal Engine: ตั้งแต่ UE 4.25 เป็นต้นไป ระบบจะเปิดใช้ R8 โดยค่าเริ่มต้นในบิลด์ที่เผยแพร่ ซึ่งกำหนดให้คุณต้องจัดการกฎการเก็บรักษาสำหรับบริดจ์และปลั๊กอิน JNI ดูข้อมูลเพิ่มเติมเกี่ยวกับรายละเอียดเวอร์ชันและการกำหนดค่า UPL ได้ที่ลดขนาดและเพิ่มประสิทธิภาพโค้ด Unreal Engine ด้วย R8

ข้อกำหนดของบิตแมป

บิตแมปเป็นส่วนสำคัญของการใช้งานหน่วยความจำในเกมสมัยใหม่ที่มีความแม่นยำสูง เนื่องจากข้อมูลพิกเซลบิตแมปจะจัดเก็บไว้ในฮีปเนทีฟที่ไม่มีการจัดการโดยตรงใน Android 8.0 (ระดับ API 26) ขึ้นไป การโหลดรูปภาพที่ไม่ได้เพิ่มประสิทธิภาพจึงอาจทำให้กระบวนการต่างๆ เกินเกณฑ์หน่วยความจำของแพลตฟอร์มได้ ดูแนวทางปฏิบัติแนะนำเกี่ยวกับการปรับขนาดรูปภาพและการแคชได้ที่หัวข้อเพิ่มประสิทธิภาพการใช้รูปภาพ

ตรวจสอบการใช้งานหน่วยความจำ

หากต้องการเพิ่มประสิทธิภาพหน่วยความจำของเกมอย่างมีประสิทธิภาพ คุณต้องเข้าใจก่อนว่าแพลตฟอร์ม Android วัดขนาดของหน่วยความจำอย่างไร Android 17 อัปเดตเมตริกหน่วยความจำเพื่อติดตามผลรวมของ RSS แบบไม่ระบุตัวตน (RssAnon) และ Swap ที่ไม่บีบอัด (VmSwap) โดยไม่รวมหน่วยความจำที่สำรองไว้ในไฟล์หรือหน่วยความจำส่วนตัวของ GPU คู่มือนี้จะอธิบายรายละเอียดเกี่ยวกับวิธีใช้เครื่องมือระดับระบบ เช่น Perfetto และ meminfo, ใช้ API การวินิจฉัย เช่น ProfilingManager และ onTrimMemory รวมถึงดึงข้อมูลการจัดสรรหน่วยความจำที่แม่นยำภายใน Unity และ Unreal Engine ทำความเข้าใจวิธีสร้างโปรไฟล์เกมอย่างถูกต้องและหลีกเลี่ยงการกระตุกของประสิทธิภาพที่เกี่ยวข้องกับการโพลหน่วยความจำรันไทม์แบบดั้งเดิม

โปรดดูข้อมูลเพิ่มเติมที่หัวข้อตรวจสอบการใช้งานหน่วยความจำ

กลยุทธ์การลดหน่วยความจำ

แม้ว่าเกมเอนจินจะช่วยลดความซับซ้อนในการพัฒนาข้ามแพลตฟอร์ม แต่การจัดการหน่วยความจำเริ่มต้นของเกมเอนจินอาจทำให้เกิดขีดจำกัดหน่วยความจำระดับระบบปฏิบัติการ หน้านี้จะอธิบายขั้นตอนการเพิ่มประสิทธิภาพที่ใช้ได้จริงซึ่งปรับแต่งมาเพื่อ Unity และ Unreal Engine โดยเฉพาะ ทำความเข้าใจว่าเหตุใดการใช้ onTrimMemory ที่ใช้ Java จึงอาจทำให้เกิด Deadlock ใน Unity และวิธีใช้ Lifecycle Callback แบบเนทีฟแทน นอกจากนี้ คุณยังจะได้ทราบถึงการเพิ่มประสิทธิภาพที่สำคัญในระดับชิ้นงาน เช่น การใช้การบีบอัดพื้นผิว ASTC 8x8 และการกำหนดค่าการยกเลิกการโหลดชิ้นงาน เพื่อให้เกมทำงานได้อย่างราบรื่นในฮาร์ดแวร์ทุกระดับ

ดูข้อมูลเพิ่มเติมได้ที่ลดการใช้งานหน่วยความจำ