ตัวจำกัดหน่วยความจำ

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
minimumRequiredMemTotal

MemTotal ของระบบขั้นต่ำ (ใน 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

การลบล้างด้วยตนเองจะมีผลเฉพาะในช่วงเวลาที่อินสแตนซ์กระบวนการนั้นทำงานอยู่เท่านั้น หากกระบวนการรีสตาร์ท ระบบจะกลับไปใช้ขีดจำกัดมาตรฐานตามสถานะของกระบวนการ

การลบล้างด้วยตนเองจะเกินขีดจำกัดของระบบจริงไม่ได้