Android 17 ขึ้นไปมีตัวจำกัดหน่วยความจำ ซึ่งเป็นบริการของระบบที่ตรวจสอบและจำกัดการใช้งานหน่วยความจำของกระบวนการแอปพลิเคชันโดยใช้ cgroup v2 ของ Linux ตัวจำกัดหน่วยความจำจะป้องกันไม่ให้แอปแต่ละแอปใช้หน่วยความจำของระบบมากเกินไป ซึ่งจะช่วยลดการใช้หน่วยความจำโดยรวมและป้องกันการหยุดกระบวนการที่ทำงานอยู่เบื้องหน้าและกระบวนการที่แคชไว้ซึ่งมีความสำคัญเนื่องจากหน่วยความจำเหลือน้อย
กลไก
ตัวจำกัดหน่วยความจำจะผสานรวมกับบริการ Activity Manager (AMS) เพื่อติดตามเหตุการณ์วงจรการทำงานของกระบวนการและการเปลี่ยนแปลงสถานะ ตัวจำกัดหน่วยความจำจะบังคับใช้ขีดจำกัดหน่วยความจำโดยใช้ลำดับชั้น cgroup v2 ของเคอร์เนล Linux
เคอร์เนลของอุปกรณ์ต้องเปิดใช้ cgroup v2 และตัวควบคุม memory เพื่อรองรับตัวจำกัดหน่วยความจำ บริการนี้อาศัยแอตทริบิวต์ต่อไปนี้โดยเฉพาะ
memory.high- ขีดจำกัดแบบไม่เข้มงวด เมื่อเกินขีดจำกัด กระบวนการจะถูกจำกัดความเร็วและเคอร์เนลจะพยายามเรียกคืนหน้าเว็บจากกระบวนการนั้นอย่างเชิงรุก
memory.swap.max- จำกัดพื้นที่สว็อปสูงสุด (เช่น ZRAM) ที่กระบวนการใช้ได้
ผลกระทบต่อแอป
แอปที่ทำงานภายในขีดจำกัดหน่วยความจำจะไม่ได้รับผลกระทบจากตัวจำกัดหน่วยความจำ
เมื่อแอปเกินขีดจำกัด memory.high เคอร์เนลจะนำหน่วยความจำที่สำรองไว้ในไฟล์ที่สะอาดของแอปออกและสว็อปหน่วยความจำที่ไม่ระบุชื่อที่ไม่ได้ใช้งานออกเพื่อให้แอปอยู่ในขีดจำกัด การนำหน้าเว็บออกและกิจกรรมการสว็อปนี้อาจทำให้แอปถูกจำกัดความเร็วในการดำเนินการชั่วคราว แต่แอปจะยังคงทำงานต่อไป
ในสถานการณ์ที่รุนแรง เช่น หน่วยความจำรั่วไหลที่ควบคุมไม่ได้ซึ่งแอปยังคงจัดสรรหน่วยความจำที่ไม่ระบุชื่อต่อไปหลังจากที่พื้นที่สว็อปหมดแล้ว แอปจะจัดสรรหน่วยความจำไม่สำเร็จและถูกยกเลิก
การตรวจสอบกระบวนการ
ตัวจำกัดหน่วยความจำจะตรวจสอบกระบวนการของแอป (UID >= 10000) โดยค่าเริ่มต้น กระบวนการของระบบหลักจะได้รับการยกเว้นเพื่อรักษาเสถียรภาพที่สำคัญของระบบ
ตัวจำกัดหน่วยความจำจะกำหนดขีดจำกัดหน่วยความจำตามสถานะการแสดงผลของกระบวนการ ดังนี้
กระบวนการที่แสดง: กระบวนการที่โฮสต์ UI ที่ผู้ใช้มองเห็นได้ (เช่น กิจกรรมที่โฟกัสอยู่ในปัจจุบันหรือการโต้ตอบที่ใช้งานอยู่) เนื่องจากการแสดง UI ต้องใช้ชุดการทำงานที่ใหญ่ขึ้นสำหรับการแสดงผลและไปป์ไลน์กราฟิก กระบวนการที่แสดงจึงได้รับขีดจำกัดหน่วยความจำที่มากขึ้น
กระบวนการที่ไม่แสดง: กระบวนการที่ทำงานในเบื้องหลังโดยไม่แสดง UI (เช่น บริการเบื้องหลังหรือตัวรับสัญญาณการออกอากาศ) เนื่องจากกระบวนการเหล่านี้ไม่ได้แสดงคอมโพเนนต์ UI จึงทำงานภายใต้ขีดจำกัดที่เข้มงวดมากขึ้น
ตารางต่อไปนี้แสดงสถานะกระบวนการที่เฉพาะเจาะจงกับการจัดประเภทขีดจำกัดหน่วยความจำ
| สถานะกระบวนการ | ขีดจำกัดหน่วยความจำ |
|---|---|
PERSISTENT | ไม่จำกัด |
PERSISTENT_UI | ไม่จำกัด |
TOP | แสดง |
BOUND_TOP | แสดง |
FOREGROUND_SERVICE | ไม่แสดง |
BOUND_FOREGROUND_SERVICE | ไม่แสดง |
IMPORTANT_FOREGROUND | แสดง |
IMPORTANT_BACKGROUND | ไม่แสดง |
TRANSIENT_BACKGROUND | ไม่แสดง |
BACKUP | ไม่แสดง |
SERVICE | ไม่แสดง |
RECEIVER | ไม่แสดง |
TOP_SLEEPING | แสดง |
HEAVY_WEIGHT | ไม่แสดง |
HOME | ไม่แสดง |
LAST_ACTIVITY | ไม่แสดง |
CACHED_ACTIVITY | แคชไว้ |
CACHED_ACTIVITY_CLIENT | แคชไว้ |
CACHED_RECENT | แคชไว้ |
CACHED_EMPTY | แคชไว้ |
ในสถานะแคช กระบวนการจะถูกหยุดชั่วคราวและเรียกคืนสูงสุด
เมื่อกระบวนการเกินขีดจำกัด memory.high ที่กำหนด ตัวจำกัดหน่วยความจำจะตรวจพบเหตุการณ์และทริกเกอร์การดำเนินการวินิจฉัย เช่น การบันทึกโปรไฟล์หน่วยความจำหรือบันทึกความผิดปกติลงใน statsd
การกำหนดค่า
ใน Android 17 ขึ้นไป ตัวจำกัดหน่วยความจำจะมีไฟล์การกำหนดค่าเริ่มต้นของแพลตฟอร์มมาตรฐานในพาร์ติชันระบบ ขณะเดียวกันก็รองรับการลบล้างที่เฉพาะเจาะจงกับอุปกรณ์ในพาร์ติชัน vendor (ไม่บังคับ)
- การกำหนดค่าเริ่มต้นของระบบ:
/system/etc/memory-limiter-config.xml - การลบล้างของผู้ให้บริการ (ไม่บังคับ):
/vendor/etc/memory-limiter-config.xml
ตัวจำกัดหน่วยความจำจะตรวจสอบ /vendor/etc/memory-limiter-config.xml ก่อน
หากมี ระบบจะใช้การกำหนดค่าของผู้ให้บริการ ไม่เช่นนั้นระบบจะกลับไปใช้การกำหนดค่าเริ่มต้นของระบบใน /system/etc/memory-limiter-config.xml
รูปแบบ XML
ไฟล์การกำหนดค่าเป็นไปตามสคีมาที่กำหนดไว้ใน memory-limiter-config.xsd ไฟล์จะกำหนดชุดขีดจำกัดหลายชุดที่จัดเรียงตามหน่วยความจำที่ใช้ได้ โดยบริการจะเลือกชุดขีดจำกัดที่ตรงกันสูงสุดตาม RAM ที่ใช้ได้ของอุปกรณ์ ค่าหน่วยความจำทั้งหมดจะระบุเป็นเมกะไบต์ (MiB)
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- จำนวนเต็มบวกที่ระบุเวอร์ชันการกำหนดค่า ค่านี้ต้องเป็น
1 minimumRequiredMemTotalMemTotalของระบบขั้นต่ำ (ใน MiB) ที่จำเป็นเพื่อให้ชุดขีดจำกัดนี้มีผล บริการจะประเมินค่านี้เทียบกับหน่วยความจำทั้งหมดที่เคอร์เนลรายงานใน/proc/meminfoทำความเข้าใจการจัดสรรหน่วยความจำ: ค่า
MemTotalของเคอร์เนลจะแสดง DRAM จริงที่ระบบปฏิบัติการเข้าถึงได้หลังจากหักการจัดสรรหน่วยความจำของฮาร์ดแวร์และเฟิร์มแวร์แล้ว การจัดสรรจะสงวน RAM ไว้สำหรับคอมโพเนนต์ฮาร์ดแวร์เฉพาะทาง เช่น GPU, โมเด็มเบสแบนด์, ISP ของกล้อง, สภาพแวดล้อมการดำเนินการที่ปลอดภัย และไฮเปอร์ไวเซอร์ ซึ่งโดยทั่วไปจะใช้หน่วยความจำจริงระหว่าง 500 MiB ถึงมากกว่า 1 GiBเนื่องจาก
MemTotalต่ำกว่า DRAM จริงที่โฆษณาไว้ของอุปกรณ์minimumRequiredMemTotalเกณฑ์จึงมีส่วนต่างสำหรับการจัดสรรเหล่านี้ ตัวอย่างเช่น การกำหนดค่าที่กำหนดเป้าหมายเป็นอุปกรณ์ 16 GB จะระบุminimumRequiredMemTotalเป็น13312(13 GiB) แทนที่จะเป็น16384(16 GiB) และระดับสำหรับอุปกรณ์ 12 GB จะระบุเป็น10240(10 GiB) ซึ่งจะช่วยให้มั่นใจว่าอุปกรณ์แต่ละเครื่องตรงกับระดับที่ต้องการไม่ว่าการจัดสรรระดับบอร์ดจะแตกต่างกันอย่างไรก็ตามmemVisibleขีดจำกัดหน่วยความจำแบบไม่เข้มงวด (
memory.high) ใน MiB ที่ใช้กับกระบวนการที่แสดงmemNotVisibleขีดจำกัดหน่วยความจำแบบไม่เข้มงวด (
memory.high) ใน MiB ที่ใช้กับกระบวนการที่ไม่แสดงswapVisibleขีดจำกัดพื้นที่สว็อป (
memory.swap.max) ใน MiB ที่ใช้กับกระบวนการที่แสดงswapNotVisibleขีดจำกัดพื้นที่สว็อป (
memory.swap.max) ใน MiB ที่ใช้กับกระบวนการที่ไม่แสดง
ขีดจำกัดแพลตฟอร์มมาตรฐาน
ใน Android 17 ขึ้นไป แพลตฟอร์มจะระบุขีดจำกัดหน่วยความจำและสว็อปมาตรฐานใน /system/etc/memory-limiter-config.xml ในระดับ RAM จริงทั่วไป ดังนี้
| RAM จริงเป้าหมาย |
MemTotal ขั้นต่ำที่จำเป็น (minimumRequiredMemTotal) |
ขีดจำกัดหน่วยความจำที่แสดง (memVisible) |
ขีดจำกัดหน่วยความจำที่ไม่แสดง (memNotVisible) |
ขีดจำกัดสว็อปที่แสดง (swapVisible) |
ขีดจำกัดสว็อปที่ไม่แสดง (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | 13,312 MiB (13 GiB) | 10,240 MiB (10 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) |
| 12 GB | 10,240 MiB (10 GiB) | 8,192 MiB (8 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) |
| 8 GB | 6,656 MiB (6.5 GiB) | 5,120 MiB (5 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) |
| 6 GB | 4,608 MiB (4.5 GiB) | 4,096 MiB (4 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) |
| 4 GB | 3,072 MiB (3 GiB) | 2,048 MiB (2 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) |
หลักการจำกัดหน่วยความจำ
การกำหนดค่าตัวจำกัดหน่วยความจำได้รับการออกแบบตามหลักการของแพลตฟอร์มต่อไปนี้
ความสอดคล้องของระบบนิเวศและความเข้ากันได้ของแอป: ขีดจำกัดหน่วยความจำที่สอดคล้องกันในอุปกรณ์ต่างๆ ช่วยให้มั่นใจได้ถึงประสิทธิภาพของแอปที่คาดการณ์ได้ทั่วทั้งระบบนิเวศ Android แอปได้รับการพัฒนาและทดสอบตามการคาดการณ์หน่วยความจำมาตรฐาน การรักษาขีดจำกัดแพลตฟอร์มที่สม่ำเสมอจะช่วยหลีกเลี่ยงการจำกัดความเร็วหรือการยกเลิกก่อนเวลาอันควร
การจัดสรรทรัพยากรตามสัดส่วน: ขีดจำกัดของแพลตฟอร์มได้รับการปรับเทียบตามความจุ RAM จริง ดังนี้
- กระบวนการที่แสดง: จัดสรร RAM จริงทั้งหมดประมาณ 1/2 ถึง 2/3 เพื่อรองรับปริมาณงาน UI, การจัดองค์ประกอบ และการแสดงผลที่ใช้งานอยู่
- กระบวนการที่ไม่แสดง: จัดสรร RAM จริงทั้งหมดประมาณ 1/4 ถึง 1/3 สำหรับงานเบื้องหลัง
การใช้งานทั่วไป: ขีดจำกัดจะมีผลกับกระบวนการแอปพลิเคชันทั้งหมดในอุปกรณ์ (UID >= 10000) อย่างสม่ำเสมอ รวมถึงแอปของระบบและแอป OEM ที่ติดตั้งไว้ล่วงหน้า ตัวจำกัดหน่วยความจำไม่รองรับรายการที่อนุญาตเพื่อยกเว้นแอปที่เฉพาะเจาะจง ซึ่งจะช่วยให้การจัดการหน่วยความจำเป็นไปอย่างยุติธรรมในทุกแอป
ไม่มี API การค้นหาที่รันไทม์: ใน Android 17 ขึ้นไป แอปจะค้นหาขีดจำกัดหน่วยความจำที่กำหนดไว้แบบเป็นโปรแกรมที่รันไทม์ไม่ได้ ขีดจำกัดแพลตฟอร์มมาตรฐานได้รับการกำหนดไว้โดยมีส่วนต่างที่มากพอเพื่อให้แอปที่ทำงานได้ดีทำงานได้โดยไม่มีข้อจำกัดระหว่างการใช้งานปกติ
การเรียกคืนและการสว็อปแบบไดนามิกของเคอร์เนล: เมื่อกระบวนการของแอปใกล้ถึงขีดจำกัด
memory.highเคอร์เนล Linux จะสั่งให้เรียกคืนหน่วยความจำที่ cgroup ของกระบวนการนั้นโดยเฉพาะ ซึ่งรวมถึงการนำหน้าเว็บที่สำรองไว้ในไฟล์ที่ไม่ได้ใช้งานออกและการสว็อปหน้าเว็บที่ไม่ระบุชื่อที่ไม่ได้ใช้งานไปยัง ZRAM ซึ่งจะช่วยรักษาความลื่นไหลของระบบโดยไม่เพิ่มการใช้หน่วยความจำทั่วโลก
คำสั่ง Shell
คำสั่ง am memory-limiter ช่วยให้นักพัฒนาซอฟต์แวร์และผู้ผสานรวมระบบโต้ตอบกับบริการที่รันไทม์เพื่อการพัฒนา การทดสอบ และการแก้ไขข้อบกพร่อง
am memory-limiter <SUB-COMMAND>สถานะ
คำสั่งย่อย status จะรายงานสถานะการทำงานและเมตริกที่ใช้งานอยู่ของตัวจำกัดหน่วยความจำ
adb shell am memory-limiter statusตัวอย่างเอาต์พุต
Memory limiter
enabled monitoring=true ignored=none
visibleMem=1948MB visibleSwap=974MB
notVisibleMem=974MB notVisibleSwap=487MB
started=36 watched=36 watch-failed=0
events=0 processes=36 process-hwm=36
ฟิลด์สำคัญในเอาต์พุต ได้แก่
monitoring- ระบุว่าตัวจำกัดหน่วยความจำกำลังตรวจสอบกระบวนการอยู่หรือไม่
visibleMemและnotVisibleMem- ขีดจำกัดหน่วยความจำสัมบูรณ์ที่คำนวณได้ (
memory.high) ซึ่งบังคับใช้ในปัจจุบันสำหรับสถานะการแสดงผลแต่ละสถานะ visibleSwapและnotVisibleSwap- ขีดจำกัดสว็อปสัมบูรณ์ที่คำนวณได้ (
memory.swap.max) ซึ่งบังคับใช้ในปัจจุบันสำหรับสถานะการแสดงผลแต่ละสถานะ events- จำนวนครั้งที่กระบวนการเกินขีดจำกัดที่กำหนดไว้
processes- จำนวนกระบวนการที่ตรวจสอบในปัจจุบัน
ไม่สนใจ
คำสั่งย่อย ignore จะยกเว้น UID ที่เฉพาะเจาะจงหรือกระบวนการทั้งหมดจากการจำกัดหน่วยความจำชั่วคราว ซึ่งมีประโยชน์สำหรับการวัดประสิทธิภาพ การทดสอบความเครียด หรือการวินิจฉัยลักษณะการทำงานของหน่วยความจำ
# Ignore a specific UID adb shell am memory-limiter ignore 10087# Ignore all processes (temporarily disables limiting) adb shell am memory-limiter ignore all# Resume normal limiting operation adb shell am memory-limiter ignore none
ด้วยตนเอง
คำสั่งย่อย manual จะลบล้างขีดจำกัดที่คำนวณได้สำหรับกระบวนการที่เฉพาะเจาะจงตามรหัสกระบวนการ (PID) ด้วยค่าสัมบูรณ์ที่กำหนดเองเป็นไบต์ ค่าต้องเป็นจำนวนเต็ม แต่สามารถมีคำต่อท้าย MB เพื่อระบุ MiB หรือคำต่อท้าย GB เพื่อระบุ GiB ได้
# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1073741824# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1024MB# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1GB# Remove the manual override for PID 1234 adb shell am memory-limiter manual 1234 none
การลบล้างด้วยตนเองจะมีผลเฉพาะในช่วงเวลาที่อินสแตนซ์กระบวนการนั้นทำงานอยู่เท่านั้น หากกระบวนการรีสตาร์ท ระบบจะกลับไปใช้ขีดจำกัดมาตรฐานตามสถานะของกระบวนการ
การลบล้างด้วยตนเองจะเกินขีดจำกัดของระบบจริงไม่ได้