Android 17 ขึ้นไปมีตัวจำกัดหน่วยความจำ ซึ่งเป็นบริการของระบบที่ตรวจสอบและจำกัดการใช้งานหน่วยความจำของกระบวนการแอปพลิเคชันโดยใช้ Linux cgroup v2 ตัวจำกัดหน่วยความจำจะป้องกันไม่ให้แอปแต่ละแอปใช้หน่วยความจำของระบบมากเกินไป ซึ่งจะช่วยลดแรงกดดันด้านหน่วยความจำโดยรวม และป้องกันการหยุดทำงานเนื่องจากหน่วยความจำไม่เพียงพอ (LMK) อย่างรุนแรงของกระบวนการที่สำคัญในเบื้องหน้าและกระบวนการที่แคชไว้
กลไก
ตัวจำกัดหน่วยความจำจะผสานรวมกับ Activity Manager Service (AMS) เพื่อติดตามเหตุการณ์ในวงจรของกระบวนการและการเปลี่ยนแปลงสถานะ ตัวจำกัดหน่วยความจำจะบังคับใช้ขีดจำกัดหน่วยความจำโดยใช้ลำดับชั้น cgroup v2 ของเคอร์เนล Linux
เคอร์เนลของอุปกรณ์ต้องเปิดใช้ cgroup v2 และตัวควบคุม memory เพื่อรองรับตัวจำกัดหน่วยความจำ บริการนี้อาศัยแอตทริบิวต์ต่อไปนี้โดยเฉพาะ
memory.high- ขีดจำกัดแบบไม่เข้มงวด เมื่อเกินขีดจำกัด กระบวนการจะถูกจำกัดและเคอร์เนลจะพยายามเรียกคืนหน้าเว็บเชิงรุกจากกระบวนการนั้น
memory.swap.max- จำกัดจำนวนพื้นที่ Swap สูงสุด (เช่น ZRAM) ที่กระบวนการสามารถใช้ได้
ผลกระทบต่อแอป
แอปที่ทำงานภายในขีดจำกัดหน่วยความจำจะไม่ได้รับผลกระทบจากตัวจำกัดหน่วยความจำ
เมื่อแอปใช้หน่วยความจำเกินขีดจำกัด memory.high เคอร์เนลจะล้างหน่วยความจำที่สำรองไว้สำหรับไฟล์ที่ไม่มีการแก้ไขของแอป และสลับหน่วยความจำที่ไม่ระบุตัวตนที่ไม่ได้ใช้งานออกเพื่อรักษาการใช้หน่วยความจำของแอปให้อยู่ภายในขีดจำกัด ผลจากการขับไล่หน้าและการสลับกิจกรรมนี้ แอปอาจพบการควบคุมการดำเนินการชั่วคราว แต่แอปจะยังคงทำงานต่อไป
ในสถานการณ์ที่รุนแรง เช่น หน่วยความจำรั่วไหลที่ควบคุมไม่ได้ซึ่งแอปยังคงจัดสรรหน่วยความจำที่ไม่ระบุชื่อหลังจากพื้นที่ Swap หมดลง แอปจะจัดสรรหน่วยความจำไม่สำเร็จและถูกสิ้นสุดการทำงาน
การตรวจสอบกระบวนการ
ตัวจำกัดหน่วยความจำจะตรวจสอบกระบวนการของแอป (UID >= 10000) โดยค่าเริ่มต้น กระบวนการหลักของระบบจะได้รับการยกเว้นเพื่อรักษาความเสถียรที่จำเป็นของระบบ
ตัวจำกัดหน่วยความจำจะกำหนดขีดจำกัดหน่วยความจำตามสถานะระดับการเข้าถึงของกระบวนการ ดังนี้
กระบวนการที่มองเห็นได้: กระบวนการที่โฮสต์ UI ที่ผู้ใช้มองเห็นได้ (เช่น กิจกรรมที่โฟกัสอยู่หรือการโต้ตอบที่ใช้งานอยู่) เนื่องจากการแสดง UI ต้องใช้ชุดการทำงานที่ใหญ่ขึ้นสำหรับการแสดงผลและไปป์ไลน์กราฟิก กระบวนการที่มองเห็นได้จึงได้รับขีดจำกัดหน่วยความจำที่มากขึ้น
กระบวนการที่มองไม่เห็น: กระบวนการที่ทำงานอยู่เบื้องหลังโดยไม่แสดง UI (เช่น บริการที่ทำงานอยู่เบื้องหลังหรือ Broadcast Receiver) เนื่องจากไม่ได้แสดงผลคอมโพเนนต์ 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 ทางกายภาพที่ระบบปฏิบัติการเข้าถึงได้หลังจากหักการแบ่งส่วนหน่วยความจำของฮาร์ดแวร์และเฟิร์มแวร์ออกแล้ว Carve-out จะสำรอง 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ขีดจำกัดพื้นที่ Swap (
memory.swap.max) ในหน่วย MiB ที่ใช้กับกระบวนการที่มองเห็นได้swapNotVisibleขีดจำกัดพื้นที่ Swap (
memory.swap.max) ในหน่วย MiB ที่ใช้กับกระบวนการที่ไม่แสดง
ขีดจำกัดมาตรฐานของแพลตฟอร์ม
ใน Android 17 ขึ้นไป แพลตฟอร์มจะระบุขีดจำกัดหน่วยความจำมาตรฐานและขีดจำกัดสวอปใน /system/etc/memory-limiter-config.xml สำหรับระดับ RAM จริงที่พบบ่อยโดยใช้ช่วง [floor, ceiling) ดังนี้
| RAM จริงเป้าหมาย |
ช่วง MemTotal [ต่ำสุด, สูงสุด) (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 ที่ติดตั้งล่วงหน้า ตัวจำกัดหน่วยความจำไม่รองรับรายการที่อนุญาตเพื่อยกเว้นแอปที่เฉพาะเจาะจง เพื่อให้มั่นใจว่ามีการกำกับดูแลหน่วยความจำอย่างเป็นธรรมในแอปทั้งหมด หากต้องการกำหนดค่าขีดจำกัดขั้นต่ำในการป้องกัน
memory.minที่ปรับแต่งเองใน Daemon แบบอินเทอร์แอกทีฟที่สำคัญ และขีดจำกัดสูงสุดmemory.highที่ปรับเทียบภาระงานให้เข้มงวดยิ่งขึ้นในแอปและบริการของระบบแต่ละรายการ โปรดดูงบประมาณหน่วยความจำของระบบไม่มี 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
คำสั่งย่อย 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
การลบล้างด้วยตนเองจะมีผลเฉพาะตลอดอายุการใช้งานของอินสแตนซ์กระบวนการนั้นๆ เท่านั้น หากกระบวนการรีสตาร์ท ระบบจะกลับไปใช้ขีดจำกัดมาตรฐานตามสถานะของกระบวนการ
การลบล้างด้วยตนเองต้องไม่เกินขีดจำกัดของระบบจริง