ใน Android 17 ขึ้นไป แพลตฟอร์มจะควบคุมหน่วยความจำของกระบวนการของระบบโดยใช้กลไก cgroup v2 ของ Linux 2 กลไกที่เสริมกันซึ่งทำงานร่วมกับตัวจำกัดหน่วยความจำของแอปพลิเคชันทั่วไป ดังนี้
- ขีดจำกัดขั้นต่ำของการปกป้อง Daemon ของระบบ (
SystemMemoryBudgetManagerที่มีmemory.min): การจองmemory.minของ cgroup v2 ที่ปรับแต่งเอง (ตั้งค่าไว้ที่ 90% ของชุดการทำงานที่อยู่ในหน่วยความจำที่คงที่ของ Daemon แต่ละรายการ และจำกัดไว้ที่ 10% ของ RAM จริงทั้งหมด) จะปกป้อง Daemon แบบอินเทอร์แอกทีฟและกระบวนการ Shell ที่สำคัญ (system_server,surfaceflinger, HAL ของกราฟิกcomposer,audioserver,cameraserver,netd,SystemUIและ Launcher ของหน้าแรก) ภายใต้แรงกดดันด้านหน่วยความจำ โดยแทนที่การปักหมุดไฟล์แบบคงที่PinnerServicemlock - เพดานหน่วยความจำของแอประบบ (
<memory-budget>ที่มีmemory.high): ขีดจำกัดแบบซอฟต์ของ cgroup v2memory.highที่ปรับเทียบภาระงาน (โดยทั่วไปคือ 30 MB ถึง 190 MB) ซึ่งประกาศไว้ในไฟล์ Manifest สำหรับแอประบบที่ทำงานอยู่ตลอดเวลาและทำงานอยู่เบื้องหลัง (Bluetooth,Nfc,CalendarProvider,Traceur,MediaProvider,:PhotoPicker,NetworkStack,AppSearchและTeleService) จะเรียกคืนแคชหน้าเว็บไฟล์ที่ไม่ได้ใช้งานและฮีปที่ไม่ระบุชื่อหลังการทำงานหนักหน่วงในเชิงรุกก่อนที่จะเกิดแรงกดดันด้านหน่วยความจำทั่วโลก
ตารางต่อไปนี้จะเปรียบเทียบกลไกทั้ง 2 แบบ
| มิติข้อมูล | ขีดจำกัดการป้องกันของกระบวนการเบื้องหลังของระบบ | เพดานหน่วยความจำของแอประบบ |
|---|---|---|
| บริการแพลตฟอร์ม | SystemMemoryBudgetManager |
MemoryLimiter |
| การควบคุม Cgroup v2 | เกณฑ์ขั้นต่ำด้านการป้องกันแบบเข้มงวด memory.min รายการ |
เพดานการเรียกคืนแบบซอฟต์ memory.high |
| กระบวนการเป้าหมาย | ดีมอนแบบอินเทอร์แอกทีฟและ UI ของเชลล์ที่สำคัญต่อเวลาในการตอบสนอง (system_server, surfaceflinger, composer HAL, audioserver, cameraserver, netd, SystemUI และแอป Home) |
แอประบบแบบถาวรและแบบไม่มีส่วนหัว, Content Provider และบริการที่ทำงานอยู่เบื้องหลัง (เช่น Bluetooth, Nfc, MediaProvider, NetworkStack, AppSearch และ TeleService) |
| โมเดลการปรับขนาด | การปรับแต่งด้วยตนเองขณะรันไทม์ (90% ของชุดการทำงานที่เสถียร โดยจำกัดไว้ที่ 10% ของ RAM ทั้งหมดทั่วโลก) โดยไม่มีตารางการปรับแต่งต่ออุปกรณ์ | แอตทริบิวต์ XML ของไฟล์ Manifest <memory-budget> แบบประกาศที่ปรับเทียบตามระดับ RAM, สถานะกระบวนการ และจำนวนพิกเซลการแสดงผล |
| ประโยชน์หลัก | ป้องกันความล่าช้าของ UI แบบอินเทอร์แอกทีฟและการหยุดชะงักของการปลุกระบบภายใต้แรงกดดันด้านหน่วยความจำขณะแทนที่รายการไฟล์ PinnerService mlock แบบคงที่ |
ล้างแคชไฟล์แบบใช้ครั้งเดียวที่ไม่ได้ใช้งานและฮีปหลังการประมวลผลแบบต่อเนื่อง เพื่อให้มี RAM จริงมากขึ้นสำหรับแอปที่ทำงานอยู่เบื้องหน้าและแอปที่แคชไว้ |
ดูข้อมูลเพิ่มเติมเกี่ยวกับไวยากรณ์ <memory-budget>, แอตทริบิวต์ XML, API รันไทม์ และการบันทึกการใช้งานหน่วยความจำของระบบปฏิบัติการได้ที่ตั้งค่างบประมาณหน่วยความจำของแอปและทำความเข้าใจและแก้ปัญหาหน่วยความจำ Android
ปกป้อง Daemon แบบอินเทอร์แอกทีฟที่สำคัญด้วย SystemMemoryBudgetManager
SystemMemoryBudgetManager จะเก็บกระบวนการที่อยู่ในเส้นทางการโต้ตอบของผู้ใช้โดยตรงไว้ใน RAM จริงในระหว่างที่หน่วยความจำมีแรงกดดัน แทนที่จะปักหมุดรายการไฟล์ APK, ODEX และ VDEX แบบคงที่ลงในหน่วยความจำด้วย mlock (เหมือนที่ PinnerService ทำกับ config_defaultPinnerServiceFiles และ config_pinnerHomePinBytes) SystemMemoryBudgetManager จะกำหนดขีดจำกัด memory.min ของ cgroup v2 แบบไดนามิกให้กับกระบวนการต่อไปนี้
- ส่วนของระบบหลัก (
/sys/fs/cgroup/system):system_server,surfaceflinger, HAL กราฟิกcomposer,audioserver,cameraserverและnetdรวมถึงระบบเนทีฟหรือ Daemon ของผู้ให้บริการที่เฉพาะเจาะจงสำหรับรูปแบบของอุปกรณ์ซึ่งแสดงอยู่ในconfig_systemMemoryBudgetAdditionalSystemProcesses(เช่นandroid.hardware.perceptionhostและauracamerahalในอุปกรณ์ XR) - ส่วนของแอปแบบอินเทอร์แอกทีฟ (
/sys/fs/cgroup/apps): กระบวนการหลักของแพ็กเกจ SystemUI ที่กำหนดค่าไว้ (config_systemUi) และกระบวนการหลักของผู้ถือบทบาทหน้าแรกของผู้ใช้ปัจจุบัน
การปกป้องผู้ถือบทบาท Home ควบคู่ไปกับ config_systemUi จะช่วยให้มั่นใจได้ว่ามีการครอบคลุมในอุปกรณ์ทุกรูปแบบ ในรูปแบบของอุปกรณ์ที่ config_systemUi เป็นแพ็กเกจความเข้ากันได้แบบเบา (เช่น Wear OS ที่หน้าปัดนาฬิกา การแจ้งเตือน และการ์ดทำงานภายในแอป Home) เชลล์ที่แสดงต่อผู้ใช้จะยังคงได้รับการปกป้องชุดการทำงานอย่างเต็มรูปแบบ หาก Intent ของหน้าแรกเปลี่ยนเป็นแพ็กเกจ config_systemUi กระบวนการจะได้รับการปกป้อง 1 ครั้ง เฉพาะกระบวนการหลักของแต่ละแพ็กเกจ (processName == packageName) เท่านั้นที่จะได้รับเกณฑ์การปกป้อง ดังนั้นกระบวนการย่อยเสริม (เช่น :screenshot) จะไม่ใช้งบประมาณการปกป้อง
นโยบายการปรับแต่งด้วยตนเองและการมอบสิทธิ์ cgroup 3 ระดับ
เนื่องจากชุดการทำงานของ Daemon แบบอินเทอร์แอกทีฟจะแตกต่างกันอย่างมากตามรูปแบบของอุปกรณ์และขนาด RAM (เช่น SystemUI ใช้ประมาณ 45 MB ในสมาร์ทวอทช์, 60 MB ในโทรศัพท์ขนาด 6 GB และ 180 MB ในโทรศัพท์ขนาด 16 GB) ตารางตรวจสอบแบบคงที่ต่ออุปกรณ์จึงไม่เสถียร แต่ SystemMemoryBudgetManager จะดึงข้อมูลพื้นโดยตรงจากฟุตพริ้นท์ที่อยู่ในหน่วยความจำของแต่ละกระบวนการโดยใช้ค่าคงที่ 3 ค่า ดังนี้
| พารามิเตอร์นโยบาย | ค่าเริ่มต้น | ลักษณะการทำงาน |
|---|---|---|
| ราคาพื้นต่อกระบวนการ | 90% ของ memory.current ที่ชำระแล้ว ลบด้วยแคชไฟล์ที่ไม่ได้แมป |
ปกป้องฮีปที่ไม่ระบุชื่อ, Slab และโค้ดที่แมปซึ่งมีการใช้งานบ่อย ขณะเดียวกันก็ปล่อยให้โค้ดที่ใช้งานน้อยที่สุด 10% สามารถเรียกคืนได้ เพื่อให้ยังคงสามารถนำหน้าเว็บที่ใช้งานน้อยออกได้ ระบบจะลบแคชไฟล์ที่ไม่ได้แมป (max(0, file - shmem - file_mapped) จาก memory.stat) ออกก่อนที่จะใช้อัตราส่วน 90% เพื่อไม่ให้แคชการสแกนแพ็กเกจการเปิดเครื่องหรือการอ่านเนื้อหาแบบครั้งเดียวทำให้ขีดจำกัดต่ำสุดเพิ่มขึ้น |
| ขีดจำกัด RAM ทั่วโลก | 10% ของ RAM ทั้งหมด (config_systemMemoryBudgetLowKb) |
ตรงกับหลักเกณฑ์ PinnerService ของ config_pinnerMaxPinnedMemoryPercentage หากผลรวมของราคาพื้น 90% เกิน 10% ของ MemTotal ระบบจะลดทรัพยากรของราคาพื้นทุกรายการตามสัดส่วนด้วยปัจจัยเดียวกัน |
| ความถี่ในการรีเฟรช | 1, 4, 16 และ 64 วินาทีหลังจากเริ่ม จากนั้นทุกๆ 30 นาที | ชุดการจัดระเบียบทางเรขาคณิตจะรวมเข้ากับชุดการทำงานหลังการเริ่มต้นระบบของ Daemon แต่ละรายการโดยไม่ต้องฮาร์ดโค้ดความล่าช้าในการวอร์มอัพต่ออุปกรณ์ และการรีเฟรชสถานะคงที่ 30 นาทีจะติดตามการเติบโตและการหดตัวของชุดการทำงานทีละน้อยตลอดอายุการใช้งานของอุปกรณ์ |
ในตัวควบคุมหน่วยความจำ Linux cgroup v2 (mm/memcontrol.c:effective_protection) การป้องกัน memory.min ที่มีผลของ Leaf cgroup จะถูกจำกัดโดย memory.min ที่กำหนดค่าไว้ใน cgroup ระดับบนและระดับบรรพบุรุษ การตั้งค่า memory.min เฉพาะในไดเรกทอรี pid_<pid> ระดับล่างสุดขณะที่ uid_<uid>/memory.min เป็น 0 จะส่งผลให้ไม่มีการป้องกันที่มีประสิทธิภาพ
ในทางกลับกัน การตั้งค่า memory.min แบบคงที่เฉพาะในส่วนบนสุดของ /sys/fs/cgroup/system จะกระจายการปกป้องที่ไม่มีการอ้างสิทธิ์ไปยัง UID ย่อยทั้งหมดและประมวลผลตามสัดส่วน (memory_recursiveprot) ซึ่งช่วยให้กระบวนการวินิจฉัยหรือกระบวนการรองที่แชร์ uid_1000 (android.uid.system) สามารถดึงการปกป้องออกจาก surfaceflinger และ audioserver ได้
เพื่อให้การแยกแบบต่อ Daemon ที่แน่นอน SystemMemoryBudgetManager จะเขียนการป้องกันในทั้ง 3 ระดับของลำดับชั้น cgroup v2 ในการรีเฟรชทุกครั้ง ดังที่แสดงในรูปที่ 1
- โหนดกระบวนการ Leaf (
pid_<pid>/memory.min): ตั้งค่าเป็นขีดจำกัดขั้นต่ำของ Working Set 90% (ปรับขนาดแล้ว) ของกระบวนการ - โหนด UID (
uid_<uid>/memory.min): ตั้งค่าเป็นผลรวมที่แน่นอนของ Leaf Floor ที่มีการจัดการpid_*ดังนั้นกระบวนการที่ไม่มีการจัดการซึ่งแชร์ UID นั้น (เช่นAID_SYSTEMDaemon อื่นๆ หรือแพ็กเกจsharedUserId="android.uid.system") จะมีmemory.min = 0และไม่สามารถอ้างสิทธิ์การป้องกันได้ - ส่วนระดับบนสุด (
/sys/fs/cgroup/system/memory.minและ/sys/fs/cgroup/apps/memory.min): ตั้งค่าเป็นผลรวมที่แน่นอนของส่วนย่อยuid_*รายการ ระบบจะล้างค่าmemory.lowที่ไม่มีการอัปเดตในโหนดที่มีการจัดการเป็น0และรีเซ็ตไดเรกทอรีuid_*ที่มีการจัดการในการรีเฟรชครั้งก่อน (เช่น ตัวเปิดหน้าแรกก่อนหน้านี้หลังจากที่ผู้ใช้เปลี่ยนตัวเปิด) เป็น0
รูปที่ 1 การมอบสิทธิ์ระดับพื้นฐานของการป้องกัน 3 ระดับในส่วนต่างๆ ของ cgroup
เหตุใดจึงใช้ memory.min แทน memory.low ในเคอร์เนล MGLRU
ใน Multi-Generational LRU (MGLRU ซึ่งเป็นเครื่องมือเรียกคืนหน้าเว็บเริ่มต้นในเคอร์เนล Android 6.6 ขึ้นไป) memory.low และ memory.min จะทำงานแตกต่างกันใน mm/vmscan.c:shrink_one ดังนี้
memory.low(การเลื่อนการเรียกคืนหน่วยความจำแบบรอบเดียว): ระบบจะหมุนเวียน cgroup ที่มีการใช้งานต่ำกว่าmemory.lowไปยังส่วนท้ายของ memcg LRU (MEMCG_LRU_TAIL) และข้ามการเรียกคืนหน่วยความจำแบบรอบเดียว ในการส่งครั้งถัดไปภายใต้แรงกดดันด้านหน่วยความจำที่ต่อเนื่อง เคอร์เนลจะเรียกคืน cgroup ทั้งหมดและบันทึกเหตุการณ์MEMCG_LOWในการทดสอบความเครียดของอุปกรณ์จริงที่มีการใช้หน่วยความจำแบบไม่ระบุตัวตนเพิ่มขึ้นเป็น 85% ของ RAM จริงmemory.lowช่วยให้SystemUIในสมาร์ทวอทช์ลดลงจากประมาณ 45 MB เหลือประมาณ 5-10 MB และaudioserverในอุปกรณ์แบบพับได้ขนาด 16 GB ลดลงจาก 15 MB เหลือ 2 MB เทียบกับระดับต่ำสุดที่ประมาณ 14 MBmemory.min(ขีดจำกัดการป้องกันขั้นต่ำ): cgroup ที่มีการใช้งานต่ำกว่าmemory.minจะถูกข้ามโดยการเรียกคืนส่วนกลาง ดังที่แสดงในรูปที่ 2 ภายใต้ภาระงานที่มีแรงกดดัน RAM 85% เท่ากันmemory.minจะกำจัดเฉพาะส่วนท้าย 10% ที่เย็นที่สุดของ Daemon แต่ละรายการ และคงSystemUI,surfaceflingerและaudioserverไว้ที่ระดับพื้น 90% โดยไม่มีข้อผิดพลาดซ้ำแบบไม่ระบุชื่อและไม่มีการสิ้นสุดกระบวนการlmkdเพิ่มขึ้น
memory.min มีการจองทางกายภาพที่เข้มงวดซึ่งตรงกับการป้องกันของการปักหมุด PinnerService mlock ที่มาแทนที่ โดยมีข้อดีคือ LRU ของเคอร์เนล (แทนที่จะเป็นรายการไฟล์แบบคงที่) จะเลือกโค้ดที่ใช้งานบ่อย, ฮีป และหน้ากราฟิกที่จะคงอยู่
รูปที่ 2 การเก็บรักษาชุดการทำงานของ Daemon ภายใต้แรงกดดันด้านหน่วยความจำที่เพิ่มขึ้น
การอุ่นเครื่องก่อนเปิดเครื่องและการเรียกคืนแคชไฟล์ที่ไม่ได้แมปหลังเปิดเครื่อง
การแทนที่ PinnerService ถาวร mlock ของ config_defaultPinnerServiceFiles ต้องประสานงานพฤติกรรมเวลาเปิดเครื่อง 2 อย่าง ได้แก่
- การเตรียมพร้อมการเปิดเครื่องในเบื้องหลังแบบครั้งเดียว (
PinnerService): ข้าม การPinnerServicemlockโดยไม่เตรียมพร้อมแคชของหน้าจะเปลี่ยนการอ่านดิสก์SystemUIAPK, ODEX และ VDEX ขนาดประมาณ 95 MB ไปยังเส้นทางการเปิดเครื่อง แบบซิงโครนัสเมื่อSystemUIเริ่มทำงาน (OnBootPhase_550) ซึ่งจะเพิ่มเวลา 300 ถึง 450 มิลลิวินาที ในการเปิดเครื่องจนเสร็จสมบูรณ์ การเรียกmadvise(MADV_WILLNEED)ไม่เพียงพอเนื่องจากforce_page_cache_raในเคอร์เนลจะจำกัดการเรียกMADV_WILLNEEDครั้งเดียวไว้ที่หน้าต่างการอ่านล่วงหน้า 1 หน้าต่าง (โดยปกติคือ 128 KB ถึง 2 MB) แต่เมื่อSystemMemoryBudgetManager.shouldSkipDefaultSystemPinningทำงานPinnerServiceจะแมปแต่ละไฟล์ในconfig_defaultPinnerServiceFilesบนเทรดเบื้องหลังระหว่างonStart, เติมไฟล์ลงในแคชของหน้าเว็บพร้อมกันด้วยOs.mlockและเรียกใช้Os.munlock,munmapและcloseทันที ซึ่งจะช่วยรักษาเวลาในการเปิดเครื่องเร็วไว้โดยไม่เหลือไบต์ที่ล็อกไว้อย่างถาวร เพื่อให้สามารถนำหน้าเริ่มต้นใช้งาน Cold Startup ออกได้ในภายหลัง - การยกเว้นและการเรียกคืนแคชการสแกนแพ็กเกจการเปิดเครื่องของ
system_server: ระหว่างการเปิดเครื่องPackageManagerServiceจะสแกน APK ที่ติดตั้งไว้ โดยปล่อยให้แคชหน้าไฟล์ที่ไม่ได้แมปแบบ Cold ขนาด 1.5 ถึง 2.5 GB ถูกเรียกเก็บเงินจาก cgroup ของsystem_serverหากรวมอยู่ในการคำนวณmemory.minของsystem_serverแคชการเปิดเครื่องใหม่นั้นจะใช้ขีดจำกัด RAM ทั่วโลก 10% จนเต็ม และลดระดับการป้องกันของsurfaceflinger,SystemUIและaudioserverตามสัดส่วน เพื่อป้องกันไม่ให้เกิดปัญหา Cap StarvationSystemMemoryBudgetManagerจะอ่านmemory.statสำหรับsystem_server, คำนวณแคชไฟล์ที่ไม่ได้แมปเป็นmax(0, file - shmem - file_mapped)และลบแคชที่ไม่ได้แมปนั้นออกจากmemory.currentก่อนที่จะคำนวณขีดจำกัดขั้นต่ำ 90% ดังที่แสดงในรูปที่ 3 นอกจากนี้ 10 วินาทีหลังจากPHASE_BOOT_COMPLETEDตัวจัดการจะเขียน<unmapped_file> swappiness=0แบบครั้งเดียวไปยังโหนดmemory.reclaimของsystem_serverในเคอร์เนล Linux 6.2 ขึ้นไปเพื่อล้างแคชการสแกนแพ็กเกจที่ไม่ได้ใช้งานอย่างต่อเนื่องโดยไม่ต้องสลับฮีปที่ไม่ระบุชื่อหรือล้าง DEX และหน้าโค้ดที่แมปไว้ ในเคอร์เนลเวอร์ชันเก่า (เช่นandroid14-6.1) ที่ปฏิเสธswappiness=0ด้วยEINVALตัวจัดการจะข้ามการเขียนmemory.reclaimแทนที่จะเปลี่ยนกลับไปใช้การเขียนเพื่อเรียกคืนที่ไม่มีคุณสมบัติเหมาะสมซึ่งจะสลับฮีปที่ไม่ระบุตัวตนออกระหว่างการเปิดเครื่อง
รูปที่ 3 การรวมระดับการป้องกันและการเรียกคืนแคชหลังจากการเปิดเครื่อง
ข้อกำหนดของเคอร์เนลและการวางซ้อนการกำหนดค่าอุปกรณ์
SystemMemoryBudgetManager อาศัย mem_cgroup_calculate_protection ในเคอร์เนล Linux เพื่อเผยแพร่ memory.min ใน cgroup ที่ซ้อนกัน เคอร์เนล Android ทั่วไป android15-6.6 และ android16-6.12 มีการรองรับการป้องกันที่ซ้อนกันของ MGLRU แบบเต็มรูปแบบตั้งแต่แกะกล่อง อุปกรณ์ที่ใช้ android14-6.1 โดยเปิดใช้ MGLRU ต้องใช้ Backport อัปสตรีม 3 รายการใน mm/vmscan.c และ mm/memcontrol.c เพื่อให้ Slice ระดับกลางที่ว่างเปล่าและ cgroup ของ UID เผยแพร่การป้องกันไปยัง cgroup ของกระบวนการ Leaf ดังนี้
30d77b7eef01(mm/mglru: fix ineffective protection calculation)8aa420617918(mm/mglru: respect min_ttl_ms with memcgs)adb8213014b2(mm: memcg: fix stale protection of reclaim target memcg)
OEM สามารถปรับแต่งหรือปิดใช้ SystemMemoryBudgetManager ได้โดยใช้การวางซ้อนทรัพยากรเฟรมเวิร์กต่อไปนี้
config_systemMemoryBudgetLowKb(จำนวนเต็ม, KB):0(ค่าเริ่มต้น) จำกัดผลรวมของmemory.minทั้งหมดไว้ที่ 10% ของ RAM จริงทั้งหมด ค่าบวกจะกำหนดขีดจำกัดที่ชัดเจนเป็น KB และค่าลบจะปิดใช้SystemMemoryBudgetManagerในอุปกรณ์ (คงการปักหมุดmlockแบบคงที่ของPinnerServiceเดิมไว้)config_systemMemoryBudgetHighKb(จำนวนเต็ม, KB):0(ค่าเริ่มต้น) จะเหลือ/sys/fs/cgroup/system/memory.highไว้ที่maxค่าบวกจะกำหนดเพดานของส่วนบนสุดmemory.high(ต้องมากกว่าหรือเท่ากับเพดานของส่วนล่างสุด)config_systemMemoryBudgetAdditionalSystemProcesses(อาร์เรย์สตริง): แสดงรายการชื่อกระบวนการเบื้องหลังของระบบหรือผู้ให้บริการแบบเนทีฟที่สำคัญต่อเวลาในการตอบสนองเพิ่มเติมใน/sys/fs/cgroup/system(จับคู่กับ/proc/<pid>/commหรือชื่อฐานของargv[0]ใน/proc/<pid>/cmdlineสำหรับ UID ของระบบและผู้ให้บริการภายใต้10000) เพื่อปกป้องด้วยระดับต่ำสุด 90% ของmemory.minควบคู่ไปกับกระบวนการเบื้องหลังของระบบเริ่มต้น 6 รายการ ใช้การวางซ้อนนี้แทนการตั้งค่าmemory.minในinit.rcเนื่องจากSystemMemoryBudgetManagerจะคำนวณuid_<uid>/memory.minใหม่เป็นผลรวมของกระบวนการ Leaf ที่มีการจัดการ และล้างโหนดuid_*ที่ไม่มีการจัดการทุกครั้งที่รีเฟรช ตัวอย่างเช่น อุปกรณ์ XR สามารถปกป้อง Daemon ของ HAL สำหรับการติดตามเชิงพื้นที่และกล้องด้วยการวางซ้อนต่อไปนี้<string-array translatable="false" name="config_systemMemoryBudgetAdditionalSystemProcesses"> <item>android.hardware.perceptionhost</item> <item>auracamerahal</item> </string-array>config_systemUi(สตริง): ชื่อแพ็กเกจของแอป SystemUI ของแพลตฟอร์ม ซึ่งแก้ไขด้วยMATCH_SYSTEM_ONLY
หากต้องการตรวจสอบชั้น SystemMemoryBudgetManager และโหนด cgroup ที่ใช้งานอยู่ในอุปกรณ์ ให้เรียกใช้คำสั่งต่อไปนี้
# Inspect policy state, resolved PIDs, and applied floors in dumpsys meminfo adb shell dumpsys meminfo | sed -n '/System Memory Budgets/,$p'# Inspect raw cgroup v2 memory.min nodes across the system and apps slices adb shell head -n 1 \ /sys/fs/cgroup/system/memory.min \ /sys/fs/cgroup/system/uid_*/memory.min \ /sys/fs/cgroup/system/uid_*/pid_*/memory.min \ /sys/fs/cgroup/apps/memory.min \ /sys/fs/cgroup/apps/uid_*/memory.min \ /sys/fs/cgroup/apps/uid_*/pid_*/memory.min
เชื่อมโยงแอปของระบบที่ทำงานอยู่ตลอดเวลาและทำงานอยู่เบื้องหลังกับงบประมาณหน่วยความจำ
แม้ว่า SystemMemoryBudgetManager จะปกป้อง Daemon แบบอินเทอร์แอกทีฟจากการเรียกคืนโดยใช้ขีดจำกัด memory.min แต่แอปอื่นๆ ของระบบและบริการแพลตฟอร์มที่มีสิทธิ์จำนวนมากก็ทำงานในเบื้องหลังเป็นเวลานานในฐานะกระบวนการ PERSISTENT, บริการที่ทำงานอยู่เบื้องหน้าที่เชื่อมโยง หรือ Content Provider ที่มีการค้นหาบ่อยครั้ง
หากไม่มีเพดานหน่วยความจำที่ชัดเจน กระบวนการเหล่านี้จะไม่ถูกจำกัดจนกว่าจะถึงขีดจำกัดตัวจำกัดหน่วยความจำหลายกิกะไบต์ (เช่น 4 GB ถึง 10 GB สำหรับแอปที่มองเห็นได้ และ 2 GB ถึง 5 GB สำหรับแอปที่ทำงานอยู่เบื้องหลัง) หรือแรงกดดันด้านหน่วยความจำทั่วโลกจะทริกเกอร์กิจกรรม kswapd ทั่วทั้งระบบและตัวปิดแอปเมื่อหน่วยความจำเหลือน้อย (lmkd)
หากไม่มีข้อจำกัด กระบวนการของระบบในเบื้องหลังมักจะสะสมหน่วยความจำแบบ Cold จำนวนมากซึ่งไม่ได้เป็นส่วนหนึ่งของชุดการทำงานที่ใช้งานอยู่
- แคชหน้าไฟล์ที่ไม่มีขอบเขต: บริการที่จัดทำดัชนีสื่อ ค้นหาฐานข้อมูล หรือซิงค์ปฏิทินและตารางผู้ให้บริการ (เช่น
AppSearch,MediaProviderและCalendarProvider) จะแมปหรือสตรีมผ่านไฟล์ขนาดใหญ่ในดิสก์ หากไม่มีเพดาน cgroupmemory.highหน้าที่สำรองข้อมูลไฟล์ที่สะอาดจากการอ่านครั้งเดียวจะยังคงถูกเรียกเก็บเงินจาก cgroup ของกระบวนการอย่างไม่มีกำหนด ซึ่งใช้ RAM จริงหลายร้อยเมกะไบต์ - การเก็บรักษาฮีปแบบไม่ระบุตัวตนหลังการทำงานแบบเบิร์ส: บริการที่พบกิจกรรมเพิ่มขึ้นอย่างรวดเร็วชั่วคราว เช่น
Bluetoothการทำงานแบบเบิร์สของการค้นหาเสียงและ BLE,NetworkStackการตรวจสอบความถูกต้องของ TLS ของแคพทีฟพอร์ทัล หรือตัวช่วย UI ที่เปลี่ยนไปทำงานในเบื้องหลัง สามารถเก็บหน้าฮีปแบบไม่ระบุตัวตนที่ไม่มีการใช้งานไว้ได้นานหลังจากที่การทำงานแบบเบิร์สเสร็จสมบูรณ์
การใช้การประกาศ <memory-budget> ที่ปรับเทียบแล้ว (ขีดจำกัดแบบซอฟต์ของ memory.high ซึ่งโดยทั่วไปคือ 30 MB ถึง 190 MB) กับแอประบบมีประโยชน์หลัก 2 ประการดังนี้
- การเรียกคืนหน้าเว็บที่แปลเป็นภาษาท้องถิ่นเชิงรุก: เมื่อกระบวนการของระบบเกินงบประมาณ
memory.highที่กำหนดค่าไว้ เคอร์เนล Linux จะกำหนดให้เรียกคืนหน้าเว็บโดยเฉพาะที่ cgroup นั้น โดยจะขับไล่หน้าเว็บที่สำรองข้อมูลไฟล์แบบ Cold ออกก่อน และบีบอัดหน้าเว็บที่ไม่ระบุตัวตนที่ไม่ได้ใช้งานไปยัง ZRAM โดยไม่ต้องรอแรงกดดันด้านหน่วยความจำของอุปกรณ์ทั่วโลก - RAM ที่พร้อมใช้งานมากขึ้นสำหรับแอปที่ทำงานอยู่เบื้องหน้าและพูลแอปที่แคชไว้: การจำกัดแคชไฟล์ที่ไม่ได้ใช้งานในบริการของระบบที่เปิดตลอดเวลาจะช่วยเรียกคืน RAM จริงหลายสิบถึงหลายร้อยเมกะไบต์ในอุปกรณ์ ซึ่งจะช่วยลดความกระตุกของ UI, ลดแรงกดดันในการเปิดกล้อง และทำให้แอปของผู้ใช้ยังคงทำงานในสถานะแคชได้มากขึ้น
กำหนดค่างบประมาณหน่วยความจำของแอประบบ
แอประบบใช้ไวยากรณ์ไฟล์ Manifest <memory-budget>, การเรียงซ้อนสถานะกระบวนการ, การปรับขนาดความหนาแน่นของการแสดงผล และกฎการแก้ไขอนุประโยคที่ตรงกันล่าสุดเหมือนกับแอปทั่วไป ดูรายการแอตทริบิวต์ทั้งหมดได้ที่ข้อมูลอ้างอิงแอตทริบิวต์ XML
OEM สามารถประกาศองค์ประกอบ <memory-budget> ภายใน AndroidManifest.xml ของแอประบบโดยตรง หรือผสานองค์ประกอบเหล่านั้นจากไฟล์ XML แยกต่างหากโดยใช้พร็อพเพอร์ตี้ additional_manifests ใน Android.bp ตามแบบแผนแล้ว AOSP จะจัดเก็บงบประมาณของแอประบบอ้างอิงไว้ในไฟล์ *-budget.xml แบบสแตนด์อโลน (ใน frameworks/base/core/memory_budgets/ สำหรับแพ็กเกจแพลตฟอร์มหลักหรือควบคู่ไปกับโมดูลแต่ละรายการ เช่น packages/modules/NetworkStack/memory_budgets/, packages/modules/AppSearch/apk/ และ packages/services/Telephony/) และแนบไฟล์เหล่านั้นด้วย additional_manifests ดังนี้
android_app {
name: "TeleService",
manifest: "AndroidManifest.xml",
additional_manifests: ["Telephony-budget.xml"],
// ...
}
การกำหนดขอบเขตระดับแพ็กเกจ (UID) เทียบกับระดับกระบวนการ (PID)
การประกาศงบประมาณพื้นฐานระดับแพ็กเกจ (<application>) หรืองบประมาณแบบหลายกระบวนการ (<processes><process>) จะกำหนดว่าเคอร์เนลจะบังคับใช้ memory.high ที่ระดับ cgroup ของ UID (/sys/fs/cgroup/uid_<uid>/memory.high) หรือที่ระดับ cgroup ต่อ PID (/sys/fs/cgroup/uid_<uid>/pid_<pid>/memory.high) ดังที่แสดงในรูปที่ 4
- งบประมาณระดับแพ็กเกจ (
<application>): ใช้เมื่อ APK เรียกใช้กระบวนการเดียวภายใต้ UID เฉพาะของตนเอง (เช่นBluetooth,Nfc,NetworkStack,CalendarProvider,TraceurหรือAppSearch) - งบประมาณระดับกระบวนการ (
<processes><process>): ใช้เมื่อ APK แชร์android:sharedUserId(เช่นandroid.uid.phoneที่มี UID1001หรือandroid.uid.systemที่มี UID1000) กับแพ็กเกจระบบอื่นๆ เพื่อไม่ให้กระบวนการอื่นๆ ที่แชร์ UID นั้นถูกจำกัดพร้อมกัน หรือเมื่อ APK เดียวเรียกใช้หลายกระบวนการที่มีบทบาทหน่วยความจำที่แตกต่างกัน (เช่น DaemonMediaProviderในเบื้องหลังและกระบวนการย่อยของ UI:PhotoPicker)
รูปที่ 4 เพดานหน่วยความจำระดับ UID เทียบกับระดับ PID
ตัวอย่างต่อไปนี้แสดง MediaProvider-budget.xml ซึ่งกำหนดขอบเขตงบประมาณที่แบ่งตามระดับ RAM ให้กับกระบวนการ MediaProvider เริ่มต้น (<process>) และงบประมาณที่ปรับขนาดตามการแสดงผลให้กับกระบวนการ UI :PhotoPicker แอตทริบิวต์ android:maxDeviceRamMb แต่ละรายการจะจับคู่อุปกรณ์ที่มี RAM จริงทั้งหมด (MemTotal) เท่ากับหรือต่ำกว่าเกณฑ์ MB ที่ระบุ (หรือใช้กับความจุ RAM ใดก็ได้เมื่อละเว้น) ระบบจะจัดเรียงส่วนคำสั่งจากค่าเริ่มต้นที่ไม่มีข้อจำกัดไปจนถึงเกณฑ์ maxDeviceRamMb ที่เล็กที่สุด เพื่อให้กฎการแก้ไขส่วนคำสั่งที่ตรงกันล่าสุดเลือกระดับที่เข้มงวดที่สุดที่เกี่ยวข้อง ดังนี้
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.providers.media.module">
<uses-sdk android:targetSdkVersion="10000" />
<application>
<processes>
<process>
<memory-budget
android:maxMb="85" />
<memory-budget
android:maxMb="75"
android:maxDeviceRamMb="12288" />
<memory-budget
android:maxMb="60"
android:maxDeviceRamMb="8192" />
</process>
<process android:process=":PhotoPicker">
<memory-budget
android:maxMb="85"
android:additionalBytesPerDisplayPixel="25" />
</process>
</processes>
</application>
</manifest>
วิธีปรับเทียบงบประมาณอ้างอิงของ AOSP ในอุปกรณ์ Google Pixel
งบประมาณหน่วยความจำของแอประบบอ้างอิงใน AOSP ได้รับการปรับเทียบในอุปกรณ์ Google Pixel ที่มี RAM จริงตั้งแต่ 6 GB ถึง 16 GB (รวมถึงโทรศัพท์ อุปกรณ์แบบพับได้ และแท็บเล็ตขนาด 6 GB, 8 GB, 12 GB และ 16 GB) และความละเอียดในการแสดงผลตั้งแต่ ~1.2 MP (720×1600) ถึง ~4.5 MP (2076×2152)
การปรับเทียบจะรวมแหล่งข้อมูลเสริม 2 แหล่งเข้าด้วยกัน ได้แก่ การวัดและส่งข้อมูลทางไกลภาคสนามของ cgroup v2 ทั่วทั้งกลุ่ม และการกวาดmemory.highอุปกรณ์จริงที่มีการควบคุมในเส้นทางของผู้ใช้ที่สำคัญ (CUJ)
การวิเคราะห์การตรวจวัดระยะไกลของ cgroup v2 ทั่วทั้งกลุ่มยานพาหนะ
ระบบจะรวบรวมสแนปชอต cgroup v2 เป็นระยะๆ ในกลุ่มอุปกรณ์และแบ่งตามระดับ RAM จริง (≤8 GB, 12 GB และ ≥16 GB) และที่เก็บข้อมูลสถานะกระบวนการ (foreground, perceptible, background และ cached) การวิเคราะห์ทางสถิติจะยึดตามหลักการ 6 ข้อต่อไปนี้
- แยกย่อย
memory.currentออกเป็นชุดการทำงานเทียบกับแคชไฟล์ที่เรียกคืนได้: เนื่องจากmemory.highควบคุมการเรียกเก็บเงิน cgroup ที่มีอยู่ (memory.current) ภาพรวมแต่ละรายการจึงถูกแยกย่อยโดยใช้memory.statออกเป็นฮีปที่ไม่ระบุชื่อ (anon) หน้าที่ไม่ระบุชื่อที่สลับ (swap) และแคชหน้าที่มีไฟล์สนับสนุน (file) ดังที่แสดงในรูปที่ 5 ความต้องการแบบไม่ระบุตัวตนทั้งหมดที่บีบอัดไม่ได้ (anon + swapที่ P99) จะกำหนดขีดจำกัดขั้นต่ำของ Working Set สำหรับงบประมาณ เพื่อไม่ให้กระบวนการมีความเสี่ยงที่จะใช้ ZRAM จนหมด ในขณะที่ช่องว่างขนาดใหญ่ระหว่างmemory.currentP99 และanon + swapP99 บ่งชี้ถึงแคชหน้าไฟล์ที่memory.highสามารถเรียกคืนได้ หากแคชนั้นไม่ได้แมปและไม่มีการใช้งาน - พิจารณาหน่วยความจำที่
memory.highไม่สามารถเรียกคืนได้: ไฟล์และหน่วยความจำที่ใช้ร่วมกันบางส่วนไม่ใช่แคชแบบ Cold หน้าที่ล็อกไว้ (unevictableในmemory.statเช่น หน่วยความจำที่ใช้ร่วมกันซึ่งล็อกด้วยmlockหรือ RAM ของผู้มาเยือนที่ล็อกไว้ของเครื่องเสมือนซึ่งกระบวนการโฮสต์ถูกเรียกเก็บเงินจาก cgroup ของแอป) จะกำหนดขีดจำกัดขั้นต่ำที่เข้มงวดภายใต้งบประมาณ และหน้าที่แมป (file_mapped) มักจะเป็นส่วนหนึ่งของชุดการทำงานที่ใช้งานอยู่ ตรวจสอบunevictable,shmemและfile_mappedก่อนที่จะถือว่าหน่วยความจำไฟล์เป็นหน่วยความจำที่เรียกคืนได้ ตัวอย่างเช่น หน่วยความจำAppSearchประมาณ 100 MB ถูกล็อกและไม่สามารถลบออกได้ ดังนั้นงบประมาณจึงครอบคลุมส่วนนั้นรวมถึงชุดการทำงานของโฮสต์ที่ใช้งานอยู่ โดยมีพื้นที่ว่างสำหรับส่วนที่ล็อกไว้เพื่อขยายขนาด - กรองการลดประสิทธิภาพในสถานะไม่มีการใช้งานออก: Content Provider (
CalendarProvider) และเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์แบบออนดีมานด์ (Traceur) ใช้เวลา 70% ถึง 99.9% ของสแนปชอตฟิลด์ที่ไม่มีการใช้งานที่ประมาณ 1 MB ถึง 3 MB การคำนวณเปอร์เซ็นไทล์ดิบในสแนปชอตทั้งหมดจะทำให้ค่าประมาณ P95 และ P99 ลดลงอย่างมาก แต่จะคำนวณเปอร์เซ็นไทล์และช่วงความเชื่อมั่นจากสแนปชอตที่ใช้งานอยู่ซึ่งไม่มีการเจือจาง (กรองสแนปชอตCACHEDและสแนปชอตพื้นฐานที่ไม่มีการใช้งานออก) - ใช้ช่วงความเชื่อมั่นที่จัดกลุ่มตามอุปกรณ์: เนื่องจากอุปกรณ์เครื่องเดียวจะให้ภาพรวมเป็นระยะๆ หลายครั้งเมื่อเวลาผ่านไป การถือว่าภาพรวมเป็นตัวอย่างอิสระจะทำให้ความไม่แน่นอนของส่วนท้ายต่ำเกินไป ค่าประมาณชุดการทำงาน P95 และ P99 ทั้งหมดได้รับการประเมินโดยใช้ช่วงความเชื่อมั่น 95% แบบคลัสเตอร์อุปกรณ์แบบอนุรักษ์นิยม (โดยใช้จำนวนอุปกรณ์ที่ไม่ซ้ำกัน
N_devเป็นขนาดตัวอย่างที่มีประสิทธิภาพ) - พิจารณาการใช้หน่วยความจำเพิ่มขึ้นอย่างรวดเร็วชั่วคราวด้วย
memory.peak: การสุ่มตัวอย่างเป็นระยะอาจพลาดการใช้หน่วยความจำเพิ่มขึ้นอย่างรวดเร็วที่มีการจัดสรรสูงและมีอายุสั้น (เช่น การตรวจสอบความถูกต้อง TLS ของแคพทีฟพอร์ทัลNetworkStackและการตรวจสอบ DNS พร้อมกัน หรือการเริ่มต้นBluetoothA2DP และ BLE) สำหรับบริการที่มีการใช้งานแบบเป็นช่วงๆ งบประมาณจะได้รับการปรับขนาดให้ครอบคลุมเปอร์เซ็นไทล์ที่ 90 ถึง 95 ของระดับสูงสุดของmemory.peakในเคอร์เนลตลอดอายุการใช้งานในระดับ RAM ต่างๆ - ยืนยันว่าระดับ RAM แสดงถึง RAM ไม่ใช่รุ่นของอุปกรณ์: อุปกรณ์รุ่นใหม่ๆ มักจะมีทั้ง RAM และฟีเจอร์มากกว่า ดังนั้นชุดการทำงานที่เพิ่มขึ้นตามระดับ RAM อาจเพิ่มขึ้นตามฟีเจอร์จริงๆ ก่อนเพิ่มระดับ
maxDeviceRamMbให้แบ่งข้อมูลตามรุ่นอุปกรณ์ ตัวอย่างเช่นcom.android.phoneมีค่า P99anon + swapเท่ากัน (~85 ถึง 90 MB) ในโทรศัพท์ 12 GB และ 16 GB ของรุ่นเดียวกัน ขณะที่โทรศัพท์แบบพับได้ 12 GB รุ่นเก่าใช้ 40 MB ดังนั้นงบประมาณจึงใช้ค่าเดียวโดยไม่มีระดับ RAM เพื่อให้อุปกรณ์ที่มี RAM ต่ำกว่าซึ่งมาพร้อมฟีเจอร์โทรศัพท์ที่ใหม่กว่ามีงบประมาณเพียงพอ
รูปที่ 5 ชุดการทำงานเทียบกับแคชไฟล์ที่เรียกคืนได้
CUJ ของอุปกรณ์จริงและการกวาดการปรับขนาดการแสดงผล
เพื่อตรวจสอบงบประมาณที่ได้จากการตรวจวัดและปรับเทียบงบประมาณตามสัดส่วนการแสดงผล (เช่น :PhotoPicker) และงบประมาณเบื้องหลังเฉพาะสถานะ (เช่น Traceur) ระบบได้ดำเนินการกวาดล้าง memory.high แบบเรียลไทม์ในอุปกรณ์จริงที่มี RAM ตั้งแต่ 6 GB ถึง 16 GB ดังนี้
- การหาจุดหักเหที่ไม่มีการเกิดข้อผิดพลาดซ้ำ: ขณะขับเคลื่อน CUJ แบบต้นทางถึงปลายทาง (เช่น การเลื่อนดูตารางกริดรูปภาพอย่างรวดเร็ว การเรียกดูอัลบั้ม การปัดดูตัวอย่างรูปภาพและวิดีโอความละเอียดสูงแบบเต็มหน้าจอ รวมถึงการบันทึกและจัดเก็บการติดตาม)
memory.highจะลดลงขณะวัดตัวนับ cgroupmemory.stat(workingset_refault_anon,workingset_refault_file,pgscanและpgsteal) และmemory.events(high) ดังที่แสดงในรูปที่ 6 งบประมาณที่เหมาะสมจะอยู่เหนือจุดหักเหที่ไม่มีการเกิดข้อผิดพลาดซ้ำเล็กน้อย จุดหักเหที่ไม่มีการเกิดข้อผิดพลาดซ้ำคือเกณฑ์ที่เคอร์เนลจะเรียกคืนแคชไฟล์แบบใช้ครั้งเดียวที่ไม่มีการใช้งาน (pgsteal > 0) โดยไม่ทำให้เกิดการ Thrashing ของชุดการทำงานที่ใช้งานอยู่ (workingset_refault == 0) - การตรวจสอบการปรับขนาดพิกเซลการแสดงผลในความละเอียดต่างๆ: การทดสอบ
:PhotoPickerในความละเอียดการแสดงผลจริงและจำลอง 4 แบบตั้งแต่ ~1.2 MP (720×1600) ถึง ~4.5 MP (จอแสดงผลด้านในที่กางออก 2076×2152) ยืนยันว่าชุดการทำงานบิตแมป Glide และ ViewPager2 ที่ใช้งานอยู่จะปรับขนาดตามจำนวนพิกเซลการแสดงผลแบบเชิงเส้น นอกจากนี้ เกณฑ์พื้นฐานที่ไม่ใช่จอแสดงผลยังแตกต่างกันไปตามอุปกรณ์ โดยที่จำนวนพิกเซลเกือบเท่ากัน จุดหักเหที่ไม่มีการแสดงผลซ้ำอยู่ที่ประมาณ 140 MB บนจอแสดงผลด้านนอกของอุปกรณ์แบบพับได้ขนาด 16 GB เทียบกับ 110 ถึง 125 MB บนโทรศัพท์ขนาด 6 GB ค่าพื้นฐาน 85 MB บวก 25 ไบต์ต่อพิกเซลการแสดงผล (android:maxMb="85"และandroid:additionalBytesPerDisplayPixel="25") จะยังคงสูงกว่าจุดหักเหที่วัดได้ในจอแสดงผลที่ทดสอบทุกจอ (ประมาณ 110 MB ใน HD+, ประมาณ 145 MB ใน FHD+ และประมาณ 190 MB ในจอแสดงผลแบบกางออก) - การยืนยันการเก็บขยะ ART ในสถานะเบื้องหลัง: ใน cgroup v2 หน้า
boot.artที่แชร์โดย Zygote และหน้าอิมเมจเฟรมเวิร์กจะยังคงถูกเรียกเก็บเงินจาก cgroup ของ Zygote และถือว่าเป็นพื้นที่ที่ปลอดภัยในระหว่างการเก็บขยะแบบ Mark-Compact (CMC) พร้อมกันของ ART การทดสอบความเครียดช่วยยืนยันว่าเมื่อแอป UI ของระบบเปลี่ยนไปใช้backgroundการจำกัดmemory.high(เช่น เป็น 30 MB ในTraceur) จะจำกัดฮีปส่วนตัวและแคชหน้าเว็บได้อย่างสมบูรณ์โดยไม่ทำให้เกิดการทริชชิง GC หรือกิจกรรมสลับ
รูปที่ 6 การค้นหาจุดที่ไม่มีการเกิดข้อผิดพลาดซ้ำในระหว่าง CUJ
สรุปงบประมาณของแอประบบอ้างอิง AOSP
ตารางต่อไปนี้สรุปงบประมาณอ้างอิงที่ปรับเทียบแล้วใน AOSP ในระดับ RAM และสถานะกระบวนการต่างๆ
| แพ็กเกจหรือกระบวนการของระบบ | ขอบเขต | RAM ≤8 GB | RAM 12 GB | RAM ≥16 GB | ไดรเวอร์การปรับเทียบหลัก |
|---|---|---|---|---|---|
com.android.bluetooth |
UID (<application>) |
80 MB | 95 MB | 110 MB | ครอบคลุมการใช้ข้อมูลสูงสุดตลอดอายุการใช้งาน P90 memory.peak (~75 ถึง 110 MB) และการใช้ข้อมูลสูงสุดขณะใช้งาน P99 memory.current มากกว่า 2 เท่า |
com.android.nfc |
UID (<application>) |
50 MB | 60 MB | 60 MB | ครอบคลุม Host Card Emulation P99 ที่ใช้งานอยู่ (สูงสุดประมาณ 50 MB) และ P90 ถึง P95 memory.peak ขณะตัดแคชไฟล์ที่ไม่ได้ใช้งาน |
com.android.providers.calendar |
UID (<application>) |
70 MB ค่าเริ่มต้น (เบื้องหน้า) 50 MB perceptible และ background |
ชุดการทำงานที่ใช้งานอยู่จะไม่แปรผันตามระดับ RAM งบประมาณเบื้องหลังจะเก็บพื้นที่ว่างที่ไม่ระบุชื่อไว้สำหรับการปลุกเพื่อช่วยเตือนความจำขณะตัดแต่งแคชหน้า SQLite ระหว่างการซิงค์ | ||
com.android.traceur |
UID (<application>) |
85 MB โดยค่าเริ่มต้น (เบื้องหน้าและรับรู้ได้) 30 MB background |
รักษาพื้นที่ว่าง 85 MB ไว้ทั้งหมดในระหว่างการบันทึกและส่งออกการติดตามที่ใช้งานอยู่ (TraceService) ขณะที่จำกัดสถานะไม่มีการใช้งานและการออกอากาศไว้ที่ 30 MB |
||
com.android.providers.media.module (หลัก) |
PID (<process>) |
60 MB | 75 MB | 85 MB | ให้พื้นที่ว่างประมาณ 10 ถึง 30 MB เหนือ P99 anon + swap ขณะเรียกคืน SQLite ที่ไม่ได้ใช้งานและแคชไฟล์ภาพขนาดย่อ |
com.android.providers.media.module:PhotoPicker |
PID (<process>) |
85 MB + 25 ไบต์ต่อพิกเซล (~110 MB ใน HD+, ~145 MB ใน FHD+, ~190 MB ใน ~4.5 MP Fold) |
ปรับขนาดตามสัดส่วนด้วยจำนวนพิกเซลที่แสดง (additionalBytesPerDisplayPixel="25") เพื่อให้ตรงกับจุดเปลี่ยนของชุดการทำงานบิตแมป Glide |
||
com.android.networkstack |
UID (<application>) |
65 MB | 70 MB | 85 MB | หน่วยความจำแบบไม่ระบุตัวตนเป็นหลัก โดยมีขนาดเท่ากับอายุการใช้งาน P95 memory.peak เพื่อให้ครอบคลุมการทำงานแบบฉับพลันของ TLS, DNS และ DHCP ของแคพทีฟพอร์ทัล |
com.android.appsearch.apk |
UID (<application>) |
130 MB | ครอบคลุมหน่วยความจำที่ล็อกไว้และไม่สามารถลบออกได้ประมาณ 100 MB บวกกับชุดการทำงานของโฮสต์ที่ใช้งานอยู่ประมาณ 5 MB โดยมีพื้นที่ว่างสำหรับขยายพื้นที่ที่ล็อกไว้ วัดการเกิดข้อผิดพลาดซ้ำเป็นศูนย์ที่ ≥125 MB ขณะที่แคชหน้าเว็บแบบ Cold (สูงสุดประมาณ 330 MB ที่ P99) ได้รับการเรียกคืน | ||
com.android.phone (TeleService) |
PID (<process>) |
120 MB | กำหนดขอบเขตต่อ PID ใน UID 1001 ที่แชร์ ชุดการทำงานจะติดตามรุ่นของอุปกรณ์แทน RAM ดังนั้นงบประมาณเดียวจะสูงกว่า P99 anon + swap สูงสุดต่อโมเดลประมาณ 30 MB |
||
วิธีที่ OEM สามารถปรับเทียบงบประมาณของแอประบบใหม่สำหรับอุปกรณ์ของตนเอง
OEM ที่ปรับแต่งแอประบบ (เช่น การเพิ่มโปรไฟล์บลูทูธของผู้ให้บริการ ส่วนขยายโทรศัพท์ที่กำหนดเอง หรือตัวจัดทำดัชนีสื่อของ OEM) หรือจัดส่งบริการของระบบที่ติดตั้งล่วงหน้าเพิ่มเติมสามารถปรับเทียบงบประมาณที่มีอยู่ใหม่หรือเขียนไฟล์ *-budget.xml ใหม่สำหรับพอร์ตโฟลิโอฮาร์ดแวร์ของตน
รวบรวมโปรไฟล์หน่วยความจำ cgroup v2 ที่ใช้งานอยู่
สำหรับกระบวนการของระบบเป้าหมายแต่ละกระบวนการ ให้อ่านโหนดหน่วยความจำ cgroup v2 ในส่วน
/sys/fs/cgroup/uid_<uid>/pid_<pid>/ ในเวิร์กโหลดและระดับ RAM ที่เป็นตัวแทน:
memory.current: หน่วยความจำ Resident ปัจจุบันที่เรียกเก็บเงินจาก cgroup (ไม่รวมหน้าเว็บที่แชร์โดย Zygote)memory.peak: จุดสูงสุดตลอดอายุการใช้งานmemory.currentจุดสูงสุดนับตั้งแต่เริ่มกระบวนการmemory.swap.current: การใช้งานสว็อปที่บีบอัดใน ZRAM ปัจจุบันmemory.stat: รายละเอียดของanon,file,pgscan,pgsteal,workingset_refault_anonและworkingset_refault_filememory.events: จำนวนสะสมของการละเมิดhighครั้ง (memory.highเหตุการณ์การควบคุมปริมาณและการเรียกคืน)
เมื่อรวบรวมข้อมูลการตรวจวัดระยะไกลจากอุปกรณ์ Dogfood หรืออุปกรณ์ภาคสนาม ให้แยกสถานะกระบวนการที่ทำงานอยู่ (TOP, FOREGROUND_SERVICE, การเชื่อมโยงที่ทำงานอยู่ และการซิงค์ที่ทำงานอยู่) ออกจากสถานะ CACHED ที่ไม่มีการใช้งานหรือสถานะ PERSISTENT ที่ไม่มีการใช้งาน เพื่อไม่ให้สแนปชอตที่ไม่มีการใช้งานทำให้การคำนวณชุดการทำงาน P95 และ P99 ของคุณลดลง
แยกฮีปที่บีบอัดไม่ได้ออกจากแคชไฟล์ที่เรียกคืนได้
เปรียบเทียบเปอร์เซ็นไทล์ที่ 99 ของ anon + swap กับเปอร์เซ็นไทล์ที่ 99 ของ memory.current ดังนี้
- บริการที่ใช้แคชไฟล์เป็นหลัก (
file≫anonเช่น ผู้ให้บริการฐานข้อมูล, ตัวจัดทำดัชนีของ Search และ Daemon บันทึก): ตั้งค่าandroid:maxMbเพื่อให้มีพื้นที่ว่างที่เหมาะสม (โดยทั่วไปคือ 1.5 เท่าถึง 4 เท่า) เหนือ P99anon + swapบวกกับหน้าต่างการทำงานสำหรับหน้าไฟล์ที่ใช้งานบ่อย การกำหนดค่านี้ช่วยให้เคอร์เนลขับหน้าไฟล์ที่ไม่ได้แมปซึ่งไม่มีการใช้งานออกอย่างต่อเนื่องโดยไม่ต้องแตะฮีปที่ไม่ระบุชื่อ หากunevictableหรือfile_mappedมีขนาดใหญ่ (เช่น หน่วยความจำที่ล็อกหรือฐานข้อมูลที่แมปหน่วยความจำ) ให้นับพื้นที่ดังกล่าวเป็นชุดการทำงานด้วย - บริการที่ใช้ฮีปแบบไม่ระบุชื่อเป็นหลักหรือบริการที่มีการใช้งานเพิ่มขึ้นอย่างรวดเร็ว (
anonเป็นส่วนใหญ่ของmemory.currentเช่น สแต็กเครือข่ายหรือการเชื่อมต่อ): ปรับขนาดandroid:maxMbให้ครอบคลุม P90 ถึง P95 ของmemory.peakเพื่อไม่ให้การใช้งานที่เพิ่มขึ้นอย่างรวดเร็วชั่วคราวทริกเกอร์การสลับ ZRAM ออกแบบซิงโครนัสในเทรดที่ไวต่อเวลาในการตอบสนอง - กระบวนการ UI ที่ใช้การแสดงผลอย่างหนัก (เช่น เครื่องมือเลือก แกลเลอรี หรือตัวช่วย UI ของระบบ): วัดขนาดหน่วยความจำพื้นฐานที่ไม่ใช่ UI และขนาดหน่วยความจำบิตแมปที่เพิ่มขึ้นต่อเมกะพิกเซลในจอแสดงผลที่มีความละเอียดต่ำสุดและสูงสุด แล้วแสดงงบประมาณโดยใช้
android:maxMbบวกandroid:additionalBytesPerDisplayPixel
กวาด memory.high ในอุปกรณ์จริง
ก่อนที่จะกำหนดงบประมาณใหม่ให้กับบิลด์ ให้ใช้ am memory-limiter manual (หรือเขียนลงในโหนด cgroup memory.high ของกระบวนการโดยตรงในบิลด์ userdebug) เพื่อทดสอบขีดจำกัดที่เป็นไปได้ขณะทำการทดสอบ CUJ อัตโนมัติ
# Override memory.high for a specific PID to 60 MB during a CUJ test
adb shell am memory-limiter manual <PID> 60MBตรวจสอบ memory.stat และ memory.events ก่อนและหลัง CUJ ดังนี้
- งบประมาณต่ำเกินไป:
workingset_refault_anonหรือworkingset_refault_fileเพิ่มขึ้นอย่างรวดเร็วระหว่าง CUJ,memory.swap.currentเพิ่มขึ้นอย่างต่อเนื่องระหว่างการทำงานที่ทำงานอยู่เบื้องหน้า หรือเฟรม CUJ หรือเวลาในการตอบสนองของ Binder ลดลง เพิ่มงบประมาณให้เกินจุดหักเหเริ่มต้น - งบประมาณสูงเกินไป:
memory.events:highยังคงเป็น0แม้ว่าจะมีการสตรีมไฟล์หรือภาระงานการจัดทำดัชนีจำนวนมาก และfileในmemory.statจะเพิ่มขึ้นอย่างไม่มีที่สิ้นสุดด้วยหน้าเว็บที่ไม่มีการเข้าชม จำกัดงบประมาณสำหรับชุดการทำงานที่ใช้งานอยู่ - งบประมาณได้รับการปรับเทียบอย่างเหมาะสม:
memory.currentในสภาวะคงตัวจะต่ำกว่าmemory.highระหว่างการทำงานปกติ ในระหว่างที่ไฟล์หรือแคชเพิ่มขึ้นอย่างรวดเร็วpgstealจะเรียกคืนหน้าไฟล์ที่ไม่ได้ใช้งาน ขณะที่workingset_refault_anonยังคง ใกล้เคียงกับศูนย์และเวลาในการตอบสนองของ CUJ ไม่เปลี่ยนแปลง