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 ขึ้นไป ตัวจำกัดหน่วยความจำจะได้รับการกำหนดค่าโดยใช้ไฟล์การกำหนดค่าแพลตฟอร์มมาตรฐานในพาร์ติชันระบบที่ /system/etc/memory-limiter-config.xml
รูปแบบ XML
ไฟล์การกำหนดค่าเป็นไปตามสคีมาที่กำหนดไว้ใน memory-limiter-config.xsd ไฟล์จะกำหนดชุดขีดจำกัดหลายชุดที่จัดเรียงตามหน่วยความจำที่ใช้ได้ โดยบริการจะเลือกชุดขีดจำกัดที่ตรงกันสูงสุดตาม RAM ที่ใช้ได้ของอุปกรณ์ ค่าหน่วยความจำทั้งหมดจะระบุเป็นเมกะไบต์ (MiB)
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM boundaries account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
<maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
<maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
<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 ถึงมากกว่า 2 GiBเนื่องจาก
MemTotalต่ำกว่า DRAM จริงที่โฆษณาไว้ของอุปกรณ์minimumRequiredMemTotalเกณฑ์จึงมีระยะขอบสำหรับการแบ่งหน่วยความจำเหล่านี้ ตัวอย่างเช่น การกำหนดค่าที่กำหนดเป้าหมายเป็นอุปกรณ์ขนาด 16 GB จะระบุพื้นเป็น14336(14 GiB) แทนที่จะเป็น16384(16 GiB) และระดับสำหรับอุปกรณ์ขนาด 12 GB จะระบุพื้นเป็น9216(9 GiB) ซึ่งจะช่วยให้มั่นใจได้ว่าอุปกรณ์แต่ละเครื่องจะตรงกับระดับที่ต้องการไม่ว่าการแบ่งหน่วยความจำระดับบอร์ดจะแตกต่างกันอย่างไรก็ตามmaximumAllowedMemTotalMemTotalสูงสุดของระบบ (ใน MiB ไม่รวมเพดาน) ที่ชุดขีดจำกัดนี้มีผล หากMemTotalมากกว่าหรือเท่ากับเพดานนี้ ชุดขีดจำกัดจะไม่มีผล สำหรับอุปกรณ์ที่มี 18,432 MiB ขึ้นไปหรืออุปกรณ์ที่มีน้อยกว่า 3,200 MiB ตัวจำกัดหน่วยความจำจะไม่ใช้ขีดจำกัด ทำให้กระบวนการไม่มีข้อจำกัด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 จริงทั่วไปโดยใช้ช่วงเวลา [floor, ceiling) ดังนี้
| RAM จริงเป้าหมาย |
ช่วง MemTotal [floor, ceiling) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
ขีดจำกัดหน่วยความจำที่แสดง (memVisible) |
ขีดจำกัดหน่วยความจำที่ไม่แสดง (memNotVisible) |
ขีดจำกัดสว็อปที่แสดง (swapVisible) |
ขีดจำกัดสว็อปที่ไม่แสดง (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | [14336, 18432) MiB |
10,240 MiB (10 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) |
| 12 GB | [9216, 14336) MiB |
8,192 MiB (8 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) |
| 8 GB | [6800, 9216) MiB |
5,120 MiB (5 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) |
| 6 GB | [4800, 6800) MiB |
4,096 MiB (4 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) |
| 4 GB | [3200, 4800) MiB |
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
การลบล้างด้วยตนเองจะมีผลเฉพาะในช่วงอายุการใช้งานของอินสแตนซ์กระบวนการที่เฉพาะเจาะจงนั้น หากกระบวนการรีสตาร์ท กระบวนการจะกลับไปใช้ขีดจำกัดมาตรฐานตามสถานะ
การลบล้างด้วยตนเองจะเกินขีดจำกัดของระบบจริงไม่ได้