Trình giới hạn bộ nhớ

Android 17 trở lên có Trình giới hạn bộ nhớ, một dịch vụ hệ thống giúp giám sát và giới hạn mức sử dụng bộ nhớ của các quy trình ứng dụng bằng Linux cgroup phiên bản 2. Trình giới hạn bộ nhớ ngăn chặn từng ứng dụng riêng lẻ tiêu thụ bộ nhớ hệ thống không cân xứng, giúp giảm áp lực bộ nhớ tổng thể và ngăn chặn việc tắt ứng dụng khi bộ nhớ thấp đối với các quy trình quan trọng ở nền trước và bộ nhớ đệm.

Cơ chế

Trình giới hạn bộ nhớ tích hợp với Dịch vụ trình quản lý hoạt động (AMS) để theo dõi các sự kiện trong vòng đời của quy trình và các thay đổi về trạng thái. Trình giới hạn bộ nhớ thực thi các giới hạn bộ nhớ bằng cách sử dụng hệ phân cấp cgroup v2 của nhân Linux.

Để hỗ trợ Trình giới hạn bộ nhớ, nhân thiết bị phải bật cgroup phiên bản 2 và bộ điều khiển memory. Dịch vụ này đặc biệt dựa vào các thuộc tính sau:

memory.high
Giới hạn linh hoạt. Khi vượt quá giới hạn, quy trình sẽ bị điều tiết và nhân hệ điều hành sẽ cố gắng chủ động thu hồi trang từ quy trình đó.
memory.swap.max
Giới hạn dung lượng tối đa của không gian hoán đổi (chẳng hạn như ZRAM) mà quy trình có thể sử dụng.

Tác động đến ứng dụng

Các ứng dụng hoạt động trong giới hạn bộ nhớ sẽ không bị ảnh hưởng bởi Trình giới hạn bộ nhớ.

Khi một ứng dụng vượt quá giới hạn memory.high, nhân hệ điều hành sẽ loại bỏ bộ nhớ dựa trên tệp sạch của ứng dụng và hoán đổi bộ nhớ ẩn danh không hoạt động để giữ cho ứng dụng nằm trong giới hạn. Do hoạt động hoán đổi và loại bỏ trang này, ứng dụng có thể bị điều tiết thực thi tạm thời, nhưng vẫn tiếp tục chạy.

Trong những trường hợp nghiêm trọng (chẳng hạn như rò rỉ bộ nhớ không kiểm soát được, trong đó ứng dụng tiếp tục phân bổ bộ nhớ ẩn danh sau khi không gian hoán đổi đã hết), ứng dụng sẽ không phân bổ được bộ nhớ và bị chấm dứt.

Giám sát quy trình

Theo mặc định, Trình giới hạn bộ nhớ sẽ giám sát các quy trình của ứng dụng (UID >= 10000). Các quy trình cốt lõi của hệ thống được miễn trừ để duy trì độ ổn định cần thiết của hệ thống.

Trình giới hạn bộ nhớ chỉ định giới hạn bộ nhớ dựa trên trạng thái hiển thị của quy trình:

  • Quy trình hiển thị: Quy trình lưu trữ giao diện người dùng mà người dùng có thể nhìn thấy (chẳng hạn như hoạt động đang được chú ý hoặc hoạt động tương tác đang diễn ra). Vì việc hiển thị giao diện người dùng đòi hỏi một tập hợp hoạt động lớn hơn cho quy trình kết xuất và đồ hoạ, nên các quy trình hiển thị sẽ nhận được giới hạn bộ nhớ lớn hơn.

  • Quy trình không hiển thị: Quy trình thực hiện công việc ở chế độ nền mà không hiển thị giao diện người dùng (chẳng hạn như dịch vụ nền hoặc bộ nhận tín hiệu truyền tin). Vì không kết xuất các thành phần giao diện người dùng, nên các tiện ích này hoạt động theo một giới hạn nghiêm ngặt hơn.

Bảng sau đây liên kết các trạng thái quy trình cụ thể với các phân loại giới hạn bộ nhớ:

Trạng thái quy trìnhGiới hạn bộ nhớ
PERSISTENTKhông hạn chế
PERSISTENT_UIKhông hạn chế
TOPĐã hiển thị
BOUND_TOPĐã hiển thị
FOREGROUND_SERVICEKhông hiển thị
BOUND_FOREGROUND_SERVICEKhông hiển thị
IMPORTANT_FOREGROUNDĐã hiển thị
IMPORTANT_BACKGROUNDKhông hiển thị
TRANSIENT_BACKGROUNDKhông hiển thị
BACKUPKhông hiển thị
SERVICEKhông hiển thị
RECEIVERKhông hiển thị
TOP_SLEEPINGĐã hiển thị
HEAVY_WEIGHTKhông hiển thị
HOMEKhông hiển thị
LAST_ACTIVITYKhông hiển thị
CACHED_ACTIVITYĐã lưu trong bộ nhớ đệm
CACHED_ACTIVITY_CLIENTĐã lưu trong bộ nhớ đệm
CACHED_RECENTĐã lưu trong bộ nhớ đệm
CACHED_EMPTYĐã lưu trong bộ nhớ đệm

Ở trạng thái lưu vào bộ nhớ đệm, các quy trình sẽ bị đóng băng và sau đó được thu hồi tối đa.

Khi một quy trình vượt quá giới hạn memory.high được chỉ định, Trình giới hạn bộ nhớ sẽ phát hiện sự kiện và có thể kích hoạt các hành động chẩn đoán, chẳng hạn như ghi lại hồ sơ bộ nhớ hoặc ghi lại điểm bất thường vào statsd.

Cấu hình

Trong Android 17 trở lên, Trình giới hạn bộ nhớ được định cấu hình bằng tệp cấu hình nền tảng tiêu chuẩn trên phân vùng hệ thống tại /system/etc/memory-limiter-config.xml.

Định dạng XML

Tệp cấu hình tuân theo giản đồ được xác định trong memory-limiter-config.xsd. Tệp này xác định nhiều nhóm giới hạn được sắp xếp theo bộ nhớ còn trống; dịch vụ sẽ chọn nhóm giới hạn phù hợp cao nhất dựa trên RAM còn trống của thiết bị. Tất cả giá trị bộ nhớ đều được chỉ định bằng mebibyte (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
Một số nguyên dương xác định phiên bản cấu hình. Giá trị này phải là 1.
minimumRequiredMemTotal

MemTotal hệ thống tối thiểu (tính bằng MiB, bao gồm cả mức tối thiểu) cần thiết để áp dụng bộ giới hạn này. Dịch vụ này đánh giá mức sử dụng bộ nhớ so với tổng bộ nhớ hiện có mà nhân hệ điều hành báo cáo trong /proc/meminfo.

Tìm hiểu về ranh giới và phần bộ nhớ được phân bổ: Giá trị hạt nhân MemTotal phản ánh DRAM vật lý mà hệ điều hành có thể truy cập sau khi trừ đi phần bộ nhớ được phân bổ cho phần cứng và chương trình cơ sở. Phần dành riêng sẽ dự trữ RAM cho các thành phần phần cứng chuyên biệt như GPU, modem băng tần cơ sở, ISP của camera, môi trường thực thi bảo mật và trình ảo hoá, thường tiêu thụ từ 500 MiB đến hơn 2 GiB bộ nhớ vật lý.

Vì MemTotal thấp hơn DRAM vật lý được quảng cáo của thiết bị, nên ngưỡng minimumRequiredMemTotal bao gồm một biên độ cho các phần bị cắt giảm này. Ví dụ: một cấu hình nhắm đến thiết bị 16 GB sẽ chỉ định mức tối thiểu là 14336 (14 GiB) thay vì 16384 (16 GiB) và một cấp cho thiết bị 12 GB sẽ chỉ định mức tối thiểu là 9216 (9 GiB). Điều này đảm bảo rằng mỗi thiết bị đều khớp với cấp dự kiến, bất kể các biến thể phân chia ở cấp bo mạch.

maximumAllowedMemTotal

MemTotal hệ thống tối đa (tính bằng MiB, không bao gồm mức trần) để áp dụng giới hạn này. Nếu MemTotal lớn hơn hoặc bằng mức trần này, thì giới hạn đã đặt sẽ không áp dụng. Đối với các thiết bị có bộ nhớ từ 18.432 MiB trở lên hoặc các thiết bị có bộ nhớ dưới 3.200 MiB, Trình giới hạn bộ nhớ sẽ không áp dụng giới hạn, khiến các quy trình không bị ràng buộc.

memVisible

Giới hạn bộ nhớ mềm (memory.high) tính bằng MiB áp dụng cho các quy trình hiển thị.

memNotVisible

Giới hạn bộ nhớ mềm (memory.high) tính bằng MiB được áp dụng cho các quy trình không hiển thị.

swapVisible

Giới hạn không gian hoán đổi (memory.swap.max) tính bằng MiB áp dụng cho các quy trình hiển thị.

swapNotVisible

Giới hạn không gian hoán đổi (memory.swap.max) tính bằng MiB được áp dụng cho các quy trình không hiển thị.

Giới hạn tiêu chuẩn của nền tảng

Trong Android 17 trở lên, nền tảng này chỉ định giới hạn bộ nhớ và hoán đổi tiêu chuẩn trong /system/etc/memory-limiter-config.xml trên các cấp RAM vật lý phổ biến bằng cách sử dụng khoảng [floor, ceiling):

RAM vật lý mục tiêu Phạm vi MemTotal [floor, ceiling)
(minimumRequiredMemTotal,
maximumAllowedMemTotal)
Giới hạn bộ nhớ hiển thị
(memVisible)
Giới hạn bộ nhớ không hiển thị
(memNotVisible)
Giới hạn hoán đổi hiển thị
(swapVisible)
Giới hạn hoán đổi không hiển thị
(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)

Nguyên tắc về giới hạn bộ nhớ

Cấu hình của Trình giới hạn bộ nhớ được thiết kế dựa trên các nguyên tắc sau đây của nền tảng:

  • Tính nhất quán của hệ sinh thái và khả năng tương thích của ứng dụng: Giới hạn bộ nhớ nhất quán trên các thiết bị đảm bảo hiệu suất ứng dụng có thể dự đoán được trong toàn bộ hệ sinh thái Android. Ứng dụng được phát triển và thử nghiệm theo mức bộ nhớ chuẩn; việc duy trì các giới hạn đồng nhất của nền tảng giúp tránh tình trạng điều tiết ngoài dự kiến hoặc chấm dứt sớm.

  • Phân bổ tài nguyên theo tỷ lệ: Giới hạn của nền tảng được điều chỉnh theo dung lượng RAM vật lý:

    • Quy trình hiển thị: Được phân bổ khoảng 1/2 đến 2/3 tổng RAM vật lý để hỗ trợ giao diện người dùng đang hoạt động, thành phần và khối lượng công việc kết xuất.
    • Quy trình không hiển thị: Được phân bổ khoảng 1/4 đến 1/3 tổng dung lượng RAM thực tế cho các tác vụ trong nền.
  • Khả năng áp dụng chung: Các giới hạn được áp dụng đồng nhất cho tất cả quy trình của ứng dụng trên thiết bị (UID >= 10000), bao gồm cả ứng dụng hệ thống và ứng dụng OEM được cài đặt sẵn. Trình giới hạn bộ nhớ không hỗ trợ danh sách cho phép để miễn trừ các ứng dụng cụ thể, đảm bảo quản lý bộ nhớ công bằng trên tất cả các ứng dụng. Để định cấu hình mức bảo vệ tối thiểu memory.min tự điều chỉnh trên các trình nền tương tác quan trọng và mức tối đa memory.high chặt chẽ hơn, được điều chỉnh theo khối lượng công việc trên từng ứng dụng và dịch vụ hệ thống, hãy xem phần Ngân sách bộ nhớ hệ thống.

  • Không có API truy vấn thời gian chạy: Trên Android 17 trở lên, ứng dụng không thể truy vấn giới hạn bộ nhớ được chỉ định theo phương thức lập trình trong thời gian chạy. Giới hạn nền tảng tiêu chuẩn được xác định với biên độ rộng để các ứng dụng hoạt động tốt mà không bị ràng buộc trong quá trình sử dụng thông thường.

  • Động lực thu hồi và hoán đổi nhân hệ điều hành: Khi một quy trình ứng dụng đạt đến giới hạn memory.high, nhân hệ điều hành Linux sẽ chỉ đạo việc thu hồi bộ nhớ cụ thể tại cgroup của quy trình đó. Điều này bao gồm việc loại bỏ các trang được hỗ trợ tệp không hoạt động và hoán đổi các trang ẩn danh không hoạt động sang ZRAM, duy trì tính linh hoạt của hệ thống mà không gây áp lực lên bộ nhớ chung.

Lệnh shell

Lệnh am memory-limiter cho phép nhà phát triển và nhà tích hợp hệ thống tương tác với dịch vụ trong thời gian chạy để phát triển, kiểm thử và gỡ lỗi:

am memory-limiter <SUB-COMMAND>

trạng thái

Lệnh phụ status báo cáo trạng thái hoạt động và các chỉ số đang hoạt động của Bộ giới hạn bộ nhớ:

adb shell am memory-limiter status

Sau đây là kết quả mẫu:

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

Các trường chính trong kết quả bao gồm:

monitoring
Cho biết liệu Trình giới hạn bộ nhớ có đang chủ động theo dõi các quy trình hay không.
visibleMem và notVisibleMem
Giới hạn bộ nhớ tuyệt đối được tính toán (memory.high) hiện được thực thi cho từng trạng thái hiển thị.
visibleSwap và notVisibleSwap
Giới hạn hoán đổi tuyệt đối được tính toán (memory.swap.max) hiện được thực thi cho từng trạng thái hiển thị.
events
Số lần một quy trình vượt quá giới hạn được chỉ định.
processes
Số lượng quy trình hiện đang được giám sát.

bỏ qua

Lệnh phụ ignore tạm thời loại trừ một UID cụ thể hoặc tất cả các quy trình khỏi giới hạn bộ nhớ. Điều này rất hữu ích cho việc đo điểm chuẩn hiệu suất, kiểm thử áp lực hoặc chẩn đoán hành vi của bộ nhớ:

# 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

thủ công

Lệnh phụ manual ghi đè giới hạn đã tính toán cho một tiến trình cụ thể theo mã định danh tiến trình (PID) bằng một giá trị tuyệt đối tuỳ chỉnh tính bằng byte. Giá trị phải là một số nguyên, nhưng có thể bao gồm hậu tố MB để biểu thị MiB hoặc hậu tố GB để biểu thị 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

Các chế độ ghi đè thủ công chỉ áp dụng cho thời gian hoạt động của phiên bản quy trình cụ thể đó. Nếu quy trình này khởi động lại, thì quy trình này sẽ trở về hạn mức tiêu chuẩn dựa trên trạng thái của quy trình.

Việc ghi đè thủ công không được vượt quá giới hạn của hệ thống vật lý.