Trong Android 17 trở lên, nền tảng này quản lý bộ nhớ của quy trình hệ thống bằng hai cơ chế cgroup v2 bổ sung của Linux, hoạt động cùng với Trình giới hạn bộ nhớ chung của ứng dụng:
- Ngưỡng bảo vệ trình nền hệ thống (
SystemMemoryBudgetManagercómemory.min): Các lượt đặt trướcmemory.mincgroup v2 tự điều chỉnh (được đặt thành 90% tập hợp hoạt động thường trú đã ổn định của mỗi trình nền và được giới hạn trên toàn hệ thống ở mức 10% tổng RAM vật lý) bảo vệ các trình nền tương tác quan trọng và quy trình shell (system_server,surfaceflinger, HAL đồ hoạcomposer,audioserver,cameraserver,netd,SystemUIvà Trình chạy chính) khi có áp lực về bộ nhớ, thay thế việc ghim tệp tĩnhPinnerServicemlock. - Giới hạn bộ nhớ của ứng dụng hệ thống (
<memory-budget>cómemory.high): Giới hạn mềmmemory.highcủa cgroup phiên bản 2 được điều chỉnh theo khối lượng công việc (thường là 30 MB đến 190 MB) được khai báo trong tệp kê khai cho các ứng dụng hệ thống cố định và chạy trong nền (Bluetooth,Nfc,CalendarProvider,Traceur,MediaProvider,:PhotoPicker,NetworkStack,AppSearchvàTeleService) chủ động thu hồi bộ nhớ đệm trang tệp không hoạt động và vùng nhớ khối xếp ẩn danh sau khi tăng đột biến trước khi xảy ra áp lực bộ nhớ toàn cục.
Bảng sau đây so sánh hai cơ chế:
| Phương diện | Mức sàn bảo vệ trình nền hệ thống | Giới hạn bộ nhớ của ứng dụng hệ thống |
|---|---|---|
| Dịch vụ nền tảng | SystemMemoryBudgetManager |
MemoryLimiter |
| Kiểm soát Cgroup v2 | memory.min giá sàn bảo vệ cứng |
memory.high soft reclaim ceiling |
| Quy trình mục tiêu | Trình nền tương tác quan trọng về độ trễ và giao diện người dùng shell (system_server, surfaceflinger, composer HAL, audioserver, cameraserver, netd, SystemUI và ứng dụng Home) |
Ứng dụng hệ thống cố định và không có giao diện người dùng, nhà cung cấp nội dung và dịch vụ nền (chẳng hạn như Bluetooth, Nfc, MediaProvider, NetworkStack, AppSearch và TeleService) |
| Mô hình định cỡ | Tự điều chỉnh trong thời gian chạy (90% tập hợp làm việc đã ổn định, giới hạn chung ở mức 10% tổng RAM) mà không cần bảng điều chỉnh cho mỗi thiết bị | Các thuộc tính XML khai báo <memory-budget> trong tệp kê khai được điều chỉnh theo cấp RAM, trạng thái quy trình và số lượng pixel hiển thị |
| Lợi ích chính | Ngăn chặn tình trạng giật giao diện người dùng tương tác và tình trạng dừng hoạt động khi khởi động trong điều kiện áp lực bộ nhớ, đồng thời thay thế danh sách tệp PinnerService mlock tĩnh |
Liên tục loại bỏ bộ nhớ đệm tệp một lần không hoạt động và vùng nhớ khối xếp sau khi tăng đột biến để dành thêm RAM vật lý cho các ứng dụng chạy trên nền trước và ứng dụng được lưu vào bộ nhớ đệm |
Để biết thêm thông tin về cú pháp <memory-budget>, thuộc tính XML, API thời gian chạy và lưu giữ hồ sơ bộ nhớ hệ điều hành, hãy xem bài viết Đặt ngân sách bộ nhớ ứng dụng và Tìm hiểu và khắc phục sự cố về bộ nhớ Android.
Bảo vệ các trình nền tương tác quan trọng bằng SystemMemoryBudgetManager
SystemMemoryBudgetManager giữ cho các quy trình nằm trực tiếp trên đường dẫn tương tác của người dùng nằm trong RAM vật lý khi bộ nhớ bị áp lực. Thay vì ghim danh sách tĩnh gồm các tệp APK, ODEX và VDEX vào bộ nhớ bằng mlock (như PinnerService thực hiện cho config_defaultPinnerServiceFiles và config_pinnerHomePinBytes), SystemMemoryBudgetManager chỉ định các mức tối thiểu memory.min của cgroup v2 động cho các quy trình sau:
- Phân đoạn hệ thống cốt lõi (
/sys/fs/cgroup/system):system_server,surfaceflinger, HAL đồ hoạcomposer,audioserver,cameraservervànetd, cùng với mọi trình nền hệ thống gốc hoặc trình nền của nhà cung cấp dành riêng cho kiểu dáng được liệt kê trongconfig_systemMemoryBudgetAdditionalSystemProcesses(ví dụ:android.hardware.perceptionhostvàauracamerahaltrên thiết bị XR). - Phần ứng dụng tương tác (
/sys/fs/cgroup/apps): Quy trình chính của gói SystemUI đã định cấu hình (config_systemUi) và quy trình chính của người dùng hiện tại giữ vai trò Trang chủ.
Việc bảo vệ người giữ vai trò Home cùng với config_systemUi đảm bảo phạm vi bao phủ trên tất cả kiểu dáng. Trên các kiểu dáng mà config_systemUi là một gói tương thích gọn nhẹ (chẳng hạn như Wear OS, nơi mặt đồng hồ, thông báo và thẻ thông tin chạy trong ứng dụng Home), shell dành cho người dùng vẫn nhận được sự bảo vệ đầy đủ của tập hợp đang hoạt động. Nếu ý định Home phân giải thành gói config_systemUi, thì quy trình sẽ được bảo vệ một lần. Chỉ quy trình chính của mỗi gói (processName == packageName) mới nhận được mức bảo vệ tối thiểu, vì vậy, các quy trình phụ trợ (chẳng hạn như :screenshot) không tiêu tốn ngân sách bảo vệ.
Chính sách tự điều chỉnh và uỷ quyền cgroup ba cấp
Vì tập hợp hoạt động của một trình nền tương tác thay đổi rất nhiều tuỳ theo kiểu dáng và dung lượng RAM (ví dụ: SystemUI sử dụng khoảng 45 MB trên đồng hồ thông minh, 60 MB trên điện thoại 6 GB và 180 MB trên điện thoại 16 GB), nên bảng tìm kiếm tĩnh cho mỗi thiết bị sẽ không ổn định. Thay vào đó, SystemMemoryBudgetManager lấy trực tiếp các giá trị tối thiểu từ dấu vết thường trú của từng quy trình bằng cách sử dụng 3 hằng số:
| Tham số chính sách | Giá trị mặc định | Hành vi |
|---|---|---|
| Giá sàn theo quy trình | 90% bộ nhớ đệm tệp chưa được ánh xạ ít hơn memory.current đã được giải quyết |
Bảo vệ vùng nhớ khối xếp ẩn danh, slab và mã được ánh xạ nóng, đồng thời cho phép thu hồi 10% đuôi lạnh nhất để vẫn có thể loại bỏ các trang thực sự lạnh. Bộ nhớ đệm tệp chưa liên kết (max(0, file - shmem - file_mapped) từ memory.stat) được trừ trước khi áp dụng tỷ lệ 90% để bộ nhớ đệm quét gói khởi động hoặc lượt đọc tài sản một lần không làm tăng mức tối thiểu. |
| Giới hạn RAM chung | 10% tổng dung lượng RAM (config_systemMemoryBudgetLowKb) |
Khớp với nguyên tắc bảo vệ PinnerService config_pinnerMaxPinnedMemoryPercentage. Nếu tổng của 90% giá sàn vượt quá 10% của MemTotal, thì mọi giá sàn sẽ được giảm theo tỷ lệ tương ứng với cùng một hệ số. |
| Tần suất làm mới | 1, 4, 16 và 64 giây sau khi bắt đầu, sau đó cứ 30 phút một lần | Chuỗi ổn định hình học hội tụ trên tập hợp hoạt động sau khi khởi động của mỗi trình nền mà không cần cố định giá trị trong mã cho độ trễ khởi động của từng thiết bị, đồng thời quá trình làm mới trạng thái ổn định trong 30 phút sẽ theo dõi mức tăng và giảm dần của tập hợp hoạt động trong suốt vòng đời của thiết bị. |
Trong bộ điều khiển bộ nhớ cgroup v2 của Linux (mm/memcontrol.c:effective_protection), mức bảo vệ memory.min hiệu quả của một cgroup lá được giới hạn bởi memory.min được định cấu hình trên cgroup mẹ và cgroup tổ tiên của nó. Việc chỉ đặt memory.min trên thư mục pid_<pid> lá trong khi uid_<uid>/memory.min là 0 sẽ không mang lại hiệu quả bảo vệ.
Ngược lại, việc đặt memory.min cố định chỉ trên lát cắt trên cùng /sys/fs/cgroup/system sẽ phân phối biện pháp bảo vệ chưa được xác nhận quyền sở hữu trên tất cả UID và quy trình con theo tỷ lệ (memory_recursiveprot), cho phép các quy trình chẩn đoán hoặc quy trình phụ chia sẻ uid_1000 (android.uid.system) để loại bỏ biện pháp bảo vệ khỏi surfaceflinger và audioserver.
Để cung cấp khả năng tách biệt chính xác cho từng trình nền, SystemMemoryBudgetManager sẽ ghi
bảo vệ trên cả ba cấp của hệ phân cấp cgroup v2 trong mỗi lần làm mới,
như minh hoạ trong hình 1:
- Nút quy trình lá (
pid_<pid>/memory.min): Đặt thành mức tối thiểu của tập hợp đang hoạt động 90% (đã điều chỉnh theo tỷ lệ) của quy trình. - Nút UID (
uid_<uid>/memory.min): Đặt thành tổng chính xác của các tầng lá được quản lýpid_*, vì vậy, các quy trình không được quản lý chia sẻ UID đó (chẳng hạn như các trình nềnAID_SYSTEMhoặc góisharedUserId="android.uid.system"khác) cómemory.min = 0và không thể yêu cầu bảo vệ. - Lát cắt cấp cao nhất (
/sys/fs/cgroup/system/memory.minvà/sys/fs/cgroup/apps/memory.min): Đặt thành tổng chính xác của các phần tử conuid_*. Mọi giá trịmemory.lowcũ trên các nút được quản lý đều được xoá thành0và mọi thư mụcuid_*được quản lý trong lần làm mới trước đó (chẳng hạn như Trình chạy màn hình chính trước đó sau khi người dùng chuyển đổi trình chạy) đều được đặt lại thành0.
Hình 1. Uỷ quyền mức bảo vệ ba cấp trên các lát cắt cgroup.
Lý do memory.min được dùng thay vì memory.low trên nhân MGLRU
Trong LRU đa thế hệ (MGLRU, công cụ lấy lại trang mặc định trên Android
6.6 và các nhân mới hơn), memory.low và memory.min hoạt động khác nhau trong
mm/vmscan.c:shrink_one:
memory.low(trì hoãn một lần): Một cgroup có mức sử dụng dướimemory.lowđược xoay vòng đến cuối LRU memcg (MEMCG_LRU_TAIL) và bị bỏ qua trong một lần thu hồi. Trong lần tiếp theo khi áp lực bộ nhớ kéo dài, hạt nhân sẽ thu hồi toàn bộ cgroup và ghi lại một sự kiệnMEMCG_LOW. Trong các bài kiểm thử áp lực trên thiết bị thực với một ứng dụng tiêu tốn bộ nhớ ẩn danh tăng lên đến 85% RAM thực,memory.lowcho phépSystemUItrên đồng hồ thông minh thu gọn từ ~45 MB xuống còn ~5 đến 10 MB vàaudioservertrên thiết bị gập 16 GB giảm từ 15 MB xuống còn 2 MB so với mức tối thiểu ~14 MB.memory.min(ngưỡng bảo vệ cứng): Một cgroup có mức sử dụng dướimemory.minsẽ bị bỏ qua trong quá trình thu hồi chung. Như trong hình 2, trong cùng một khối lượng công việc áp lực RAM 85%,memory.minchỉ loại bỏ 10% phần đuôi ít hoạt động nhất của mỗi trình nền và giữSystemUI,surfaceflingervàaudioserverở mức tối thiểu 90% mà không có lỗi lặp lại ẩn danh và không tăng số lần chấm dứt quy trìnhlmkd.
memory.min cung cấp một mức đặt trước vật lý cứng khớp với mức bảo vệ của tính năng ghim PinnerService mlock mà nó thay thế, với ưu điểm là LRU nhân (thay vì danh sách tệp tĩnh) sẽ chọn mã nóng, vùng nhớ khối xếp và trang đồ hoạ nào sẽ được lưu trú.
Hình 2. Duy trì tập hợp hoạt động của trình nền khi áp lực bộ nhớ tăng lên.
Làm nóng trước khi khởi động và thu hồi bộ nhớ đệm tệp chưa được ánh xạ sau khi khởi động
Việc thay thế PinnerService vĩnh viễn mlock của config_defaultPinnerServiceFiles đòi hỏi phải điều phối hai hành vi trong thời gian khởi động:
- Làm nóng trước khi khởi động trong nền một lần (
PinnerService): Việc bỏ quaPinnerServicemlockmà không làm nóng bộ nhớ đệm trang sẽ chuyển khoảng 95 MB dữ liệu đọc ổ đĩa APK, ODEX và VDEX củaSystemUIsang đường dẫn khởi động đồng bộ khiSystemUIkhởi động (OnBootPhase_550), khiến thời gian hoàn tất khởi động tăng thêm từ 300 đến 450 mili giây. Việc gọimadvise(MADV_WILLNEED)là chưa đủ vìforce_page_cache_ratrong hạt nhân sẽ kẹp một lệnh gọiMADV_WILLNEEDvào một cửa sổ đọc trước (thường là 128 KB đến 2 MB). Thay vào đó, khiSystemMemoryBudgetManager.shouldSkipDefaultSystemPinningđang hoạt động,PinnerServicesẽ liên kết từng tệp trongconfig_defaultPinnerServiceFilestrên một luồng trong nền trongonStart, đồng bộ hoá tệp vào bộ nhớ đệm trang bằngOs.mlockvà gọi ngayOs.munlock,munmapvàclose. Cách này giúp duy trì thời gian khởi động nhanh trong khi không để lại byte nào bị khoá vĩnh viễn, nhờ đó, các trang khởi động nguội có thể bị loại bỏ sau này. - Loại trừ và thu hồi bộ nhớ đệm quét gói khởi động
system_server: Trong quá trình khởi động,PackageManagerServicesẽ quét các tệp APK đã cài đặt, để lại 1,5 đến 2,5 GB bộ nhớ đệm trang tệp chưa được liên kết nguội được tính vào cgroupsystem_server. Nếu được đưa vào phép tínhmemory.mincủasystem_server, thì bộ nhớ đệm khởi động nguội đó sẽ lấp đầy giới hạn RAM chung 10% và giảm tỷ lệ tương ứng với mức bảo vệ tối thiểu củasurfaceflinger,SystemUIvàaudioserver. Để ngăn tình trạng thiếu hụt giới hạn,SystemMemoryBudgetManagerđọcmemory.statchosystem_server, tính toán bộ nhớ đệm tệp chưa được liên kết dưới dạngmax(0, file - shmem - file_mapped)và trừ bộ nhớ đệm chưa được liên kết đó khỏimemory.currenttrước khi tính toán mức sàn 90%, như minh hoạ trong hình 3. Ngoài ra, 10 giây sauPHASE_BOOT_COMPLETED, trình quản lý sẽ thực hiện một lần ghi<unmapped_file> swappiness=0vào nútmemory.reclaimcủasystem_servertrên Linux 6.2 và các nhân mới hơn để chủ động loại bỏ bộ nhớ đệm quét gói không hoạt động mà không cần hoán đổi vùng nhớ khối xếp ẩn danh hoặc loại bỏ DEX và trang mã được liên kết. Trên các hạt nhân cũ (chẳng hạn nhưandroid14-6.1) từ chốiswappiness=0bằngEINVAL, trình quản lý sẽ bỏ qua thao tác ghimemory.reclaimthay vì quay lại thao tác ghi thu hồi không đủ điều kiện để hoán đổi vùng nhớ khối xếp ẩn danh trong quá trình khởi động.
Hình 3. Hội tụ tầng bảo vệ và thu hồi bộ nhớ đệm sau khi khởi động.
Yêu cầu về hạt nhân và lớp phủ cấu hình thiết bị
SystemMemoryBudgetManager dựa vào mem_cgroup_calculate_protection trong nhân Linux để truyền memory.min qua các nhóm điều khiển lồng nhau. Nhân Android chung android15-6.6 và android16-6.12 có hỗ trợ đầy đủ tính năng bảo vệ lồng nhau của MGLRU ngay từ đầu. Các thiết bị chạy android14-6.1 đã bật MGLRU cần có 3 bản backport ngược dòng trong mm/vmscan.c và mm/memcontrol.c để các lát cắt trung gian trống và cgroup UID truyền biện pháp bảo vệ đến các cgroup quy trình lá:
30d77b7eef01(mm/mglru: fix ineffective protection calculation)8aa420617918(mm/mglru: respect min_ttl_ms with memcgs)adb8213014b2(mm: memcg: fix stale protection of reclaim target memcg)
OEM có thể tuỳ chỉnh hoặc tắt SystemMemoryBudgetManager bằng cách sử dụng các lớp phủ tài nguyên khung sau:
config_systemMemoryBudgetLowKb(số nguyên, KB):0(mặc định) giới hạn tổng của tất cả các tầngmemory.minở mức 10% tổng dung lượng RAM thực. Giá trị dương đặt giới hạn rõ ràng theo KB và giá trị âm tắtSystemMemoryBudgetManagertrên thiết bị (giữ lại tính năng ghimmlocktĩnhPinnerServicecũ).config_systemMemoryBudgetHighKb(số nguyên, KB):0(mặc định) giữ nguyên/sys/fs/cgroup/system/memory.highở mứcmax. Giá trị dương đặt mức trần cho phần trên cùngmemory.high(phải lớn hơn hoặc bằng mức sàn).config_systemMemoryBudgetAdditionalSystemProcesses(string-array): Liệt kê tên của các trình nền hệ thống hoặc nhà cung cấp gốc có độ trễ nghiêm trọng khác trong/sys/fs/cgroup/system(khớp với/proc/<pid>/commhoặc tên cơ sở củaargv[0]trong/proc/<pid>/cmdlinecho UID của hệ thống và nhà cung cấp trong10000) để bảo vệ bằng 90%memory.mincùng với 6 trình nền hệ thống mặc định. Sử dụng lớp phủ này thay vì đặtmemory.mintronginit.rcvìSystemMemoryBudgetManagertính toán lạiuid_<uid>/memory.mindưới dạng tổng của các quy trình lá được quản lý và xoá các nútuid_*không được quản lý trong mỗi lần làm mới. Ví dụ: một thiết bị XR có thể bảo vệ trình nền HAL của camera và tính năng theo dõi không gian bằng lớp phủ sau:<string-array translatable="false" name="config_systemMemoryBudgetAdditionalSystemProcesses"> <item>android.hardware.perceptionhost</item> <item>auracamerahal</item> </string-array>config_systemUi(chuỗi): Tên gói của ứng dụng SystemUI trên nền tảng, được phân giải bằngMATCH_SYSTEM_ONLY.
Để kiểm tra các tầng SystemMemoryBudgetManager đang hoạt động và các nút cgroup trên một thiết bị, hãy chạy:
# Inspect policy state, resolved PIDs, and applied floors in dumpsys meminfo adb shell dumpsys meminfo | sed -n '/System Memory Budgets/,$p'# Inspect raw cgroup v2 memory.min nodes across the system and apps slices adb shell head -n 1 \ /sys/fs/cgroup/system/memory.min \ /sys/fs/cgroup/system/uid_*/memory.min \ /sys/fs/cgroup/system/uid_*/pid_*/memory.min \ /sys/fs/cgroup/apps/memory.min \ /sys/fs/cgroup/apps/uid_*/memory.min \ /sys/fs/cgroup/apps/uid_*/pid_*/memory.min
Giới hạn các ứng dụng hệ thống chạy ngầm và liên tục bằng ngân sách bộ nhớ
Mặc dù SystemMemoryBudgetManager bảo vệ trình nền tương tác khỏi việc thu hồi bằng cách sử dụng các tầng memory.min, nhưng nhiều ứng dụng hệ thống và dịch vụ nền tảng đặc quyền khác chạy trong nền trong thời gian dài dưới dạng quy trình PERSISTENT, dịch vụ nền trước liên kết hoặc trình cung cấp nội dung thường xuyên được truy vấn.
Nếu không có giới hạn bộ nhớ rõ ràng, các quy trình này sẽ không bị ràng buộc cho đến khi đạt đến giới hạn Trình giới hạn bộ nhớ nhiều gigabyte (chẳng hạn như 4 GB đến 10 GB cho ứng dụng hiển thị và 2 GB đến 5 GB cho ứng dụng chạy trong nền) hoặc áp lực bộ nhớ chung kích hoạt hoạt động kswapd và trình tắt ứng dụng khi bộ nhớ thấp (lmkd) trên toàn hệ thống.
Nếu không bị ràng buộc, các quy trình hệ thống trong nền thường tích luỹ một lượng lớn bộ nhớ nguội không thuộc tập hợp hoạt động của chúng:
- Bộ nhớ đệm trang tệp không giới hạn: Các dịch vụ lập chỉ mục nội dung nghe nhìn, tìm kiếm cơ sở dữ liệu hoặc đồng bộ hoá lịch và bảng nhà cung cấp (chẳng hạn như
AppSearch,MediaProvidervàCalendarProvider) ánh xạ hoặc truyền trực tuyến qua các tệp lớn trên ổ đĩa. Nếu không có giới hạnmemory.highcủa nhóm điều khiển, thì các trang sạch được hỗ trợ bằng tệp từ các lượt đọc một lần sẽ vẫn được tính vào nhóm điều khiển của quy trình vô thời hạn, tiêu tốn hàng trăm megabyte RAM vật lý. - Giữ lại vùng nhớ khối xếp ẩn danh sau khi tăng đột biến: Các dịch vụ gặp phải tình trạng tăng đột biến tạm thời, chẳng hạn như
Bluetoothtăng đột biến về âm thanh và khám phá BLE,NetworkStackxác thực TLS của trang xác thực hoặc trình trợ giúp giao diện người dùng chuyển sang chế độ nền, có thể giữ lại các trang vùng nhớ khối xếp ẩn danh không hoạt động trong thời gian dài sau khi quá trình tăng đột biến hoàn tất.
Việc áp dụng các nội dung khai báo <memory-budget> đã hiệu chỉnh (giới hạn mềm memory.high, thường là 30 MB đến 190 MB) cho các ứng dụng hệ thống mang lại hai lợi ích chính:
- Thu hồi trang cục bộ, chủ động: Khi một quy trình hệ thống vượt quá ngân sách
memory.highđã định cấu hình, nhân Linux sẽ hướng việc thu hồi trang cụ thể đến cgroup đó, loại bỏ các trang được hỗ trợ tệp lạnh trước và nén các trang ẩn danh không hoạt động vào ZRAM mà không cần chờ áp lực bộ nhớ thiết bị chung. - Tăng dung lượng RAM cho các ứng dụng trên nền trước và nhóm ứng dụng được lưu vào bộ nhớ đệm: Việc giới hạn bộ nhớ đệm tệp nguội trên các dịch vụ hệ thống luôn bật sẽ giải phóng hàng chục đến hàng trăm megabyte RAM vật lý trên thiết bị, giảm hiện tượng giật giao diện người dùng, giảm áp lực khi khởi động camera và duy trì nhiều ứng dụng của người dùng hơn ở trạng thái lưu vào bộ nhớ đệm.
Định cấu hình ngân sách bộ nhớ của ứng dụng hệ thống
Ứng dụng hệ thống sử dụng cùng một cú pháp tệp kê khai <memory-budget>, phân tầng trạng thái quy trình, điều chỉnh tỷ lệ mật độ hiển thị và quy tắc phân giải mệnh đề khớp cuối cùng như các ứng dụng thông thường. Để xem danh sách đầy đủ các thuộc tính, hãy xem Tài liệu tham khảo về thuộc tính XML.
OEM có thể khai báo các phần tử <memory-budget> ngay bên trong AndroidManifest.xml của ứng dụng hệ thống hoặc hợp nhất các phần tử đó từ một tệp XML riêng biệt bằng cách sử dụng thuộc tính additional_manifests trong Android.bp. Theo quy ước, AOSP lưu trữ ngân sách ứng dụng hệ thống tham chiếu trong các tệp *-budget.xml độc lập (trong frameworks/base/core/memory_budgets/ cho các gói nền tảng cốt lõi hoặc cùng với các mô-đun riêng lẻ như packages/modules/NetworkStack/memory_budgets/, packages/modules/AppSearch/apk/ và packages/services/Telephony/) và đính kèm chúng với additional_manifests:
android_app {
name: "TeleService",
manifest: "AndroidManifest.xml",
additional_manifests: ["Telephony-budget.xml"],
// ...
}
Phạm vi cấp gói (UID) so với cấp quy trình (PID)
Việc bạn khai báo ngân sách cơ sở ở cấp gói (<application>) hay ngân sách đa quy trình (<processes><process>) sẽ xác định xem hạt nhân có thực thi memory.high ở cấp cgroup UID (/sys/fs/cgroup/uid_<uid>/memory.high) hay ở cấp cgroup theo PID (/sys/fs/cgroup/uid_<uid>/pid_<pid>/memory.high) hay không, như minh hoạ trong hình 4:
- Ngân sách cấp gói (
<application>): Sử dụng khi APK chạy một quy trình duy nhất theo UID chuyên dụng của riêng nó (ví dụ:Bluetooth,Nfc,NetworkStack,CalendarProvider,TraceurhoặcAppSearch). - Ngân sách cấp quy trình (
<processes><process>): Sử dụng khi APK chia sẻandroid:sharedUserId(chẳng hạn nhưandroid.uid.phonecó UID1001hoặcandroid.uid.systemcó UID1000) với các gói hệ thống khác để các quy trình khác chia sẻ UID đó không bị điều tiết cùng nhau, hoặc khi một APK chạy nhiều quy trình có vai trò bộ nhớ riêng biệt (ví dụ: trình nềnMediaProvidertrong nền và quy trình con giao diện người dùng:PhotoPicker).
Hình 4. Giới hạn bộ nhớ cấp UID so với cấp PID.
Ví dụ sau đây minh hoạ MediaProvider-budget.xml, trong đó xác định phạm vi ngân sách theo cấp RAM cho quy trình MediaProvider mặc định (<process>) và ngân sách theo tỷ lệ hiển thị cho quy trình giao diện người dùng :PhotoPicker. Mỗi thuộc tính android:maxDeviceRamMb khớp với các thiết bị có tổng RAM vật lý (MemTotal) bằng hoặc thấp hơn ngưỡng MB đã chỉ định (hoặc áp dụng cho bất kỳ dung lượng RAM nào khi bị bỏ qua). Các mệnh đề được sắp xếp từ mặc định không ràng buộc xuống ngưỡng maxDeviceRamMb nhỏ nhất để quy tắc phân giải mệnh đề khớp cuối cùng chọn cấp áp dụng chặt chẽ nhất:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.providers.media.module">
<uses-sdk android:targetSdkVersion="10000" />
<application>
<processes>
<process>
<memory-budget
android:maxMb="85" />
<memory-budget
android:maxMb="75"
android:maxDeviceRamMb="12288" />
<memory-budget
android:maxMb="60"
android:maxDeviceRamMb="8192" />
</process>
<process android:process=":PhotoPicker">
<memory-budget
android:maxMb="85"
android:additionalBytesPerDisplayPixel="25" />
</process>
</processes>
</application>
</manifest>
Cách hiệu chỉnh ngân sách tham chiếu của AOSP trên thiết bị Google Pixel
Ngân sách bộ nhớ của ứng dụng hệ thống tham chiếu trong AOSP được hiệu chỉnh trên các thiết bị Google Pixel có RAM vật lý từ 6 GB đến 16 GB (bao gồm cả điện thoại, thiết bị gập và máy tính bảng có RAM 6 GB, 8 GB, 12 GB và 16 GB) và độ phân giải màn hình từ ~1, 2 MP (720×1600) đến ~4, 5 MP (2076×2152).
Quá trình hiệu chỉnh kết hợp hai nguồn dữ liệu bổ sung: dữ liệu đo từ xa của trường cgroup v2 trên toàn bộ nhóm thiết bị và các lần quét memory.high trên thiết bị thực được kiểm soát trong các hành trình quan trọng của người dùng (CUJ).
Phân tích dữ liệu đo từ xa cgroup v2 trên toàn bộ nhóm máy chủ
Ảnh chụp nhanh định kỳ của cgroup phiên bản 2 được thu thập trên toàn bộ nhóm thiết bị và được phân chia theo cấp RAM vật lý (≤8 GB, 12 GB và ≥16 GB) và bộ chứa trạng thái quy trình (foreground, perceptible, background và cached). Có 6 nguyên tắc hướng dẫn việc phân tích thống kê:
- Phân tách
memory.currentthành tập hợp đang hoạt động so với bộ nhớ đệm tệp có thể thu hồi: Vìmemory.highchi phối mức phí cgroup thường trú (memory.current), nên mỗi ảnh chụp nhanh được phân tách bằngmemory.statthành vùng nhớ khối xếp ẩn danh (anon), các trang ẩn danh được hoán đổi (swap) và bộ nhớ đệm trang dựa trên tệp (file), như minh hoạ trong hình 5. Tổng nhu cầu ẩn danh không thể nén (anon + swaptại P99) thiết lập mức tối thiểu của tập hợp đang hoạt động cho ngân sách để quy trình không có nguy cơ làm cạn kiệt ZRAM, trong khi khoảng cách lớn giữamemory.currentP99 vàanon + swapP99 cho biết bộ nhớ đệm trang tệp màmemory.highcó thể thu hồi, miễn là bộ nhớ đệm đó chưa được ánh xạ và không hoạt động. - Tính đến bộ nhớ mà
memory.highkhông thể lấy lại: Không phải tất cả tệp và bộ nhớ dùng chung đều là bộ nhớ đệm không hoạt động. Các trang bị khoá (unevictabletrongmemory.stat, chẳng hạn như bộ nhớ dùng chung bị khoá bằngmlockhoặc RAM khách bị khoá của một máy ảo có quy trình lưu trữ được tính vào cgroup của ứng dụng) đặt một mức sàn cứng trong ngân sách và các trang được liên kết (file_mapped) thường là một phần của tập hợp hoạt động đang hoạt động. Kiểm traunevictable,shmemvàfile_mappedtrước khi coi bộ nhớ tệp là có thể lấy lại. Ví dụ: khoảng 100 MB bộ nhớAppSearchbị khoá và không thể loại bỏ, vì vậy, ngân sách của bộ nhớ này bao gồm mức tối thiểu đó cộng với tập hợp hoạt động của máy chủ lưu trữ nóng, với khoảng trống để vùng bị khoá tăng lên. - Lọc bỏ tình trạng pha loãng trạng thái rảnh: Nhà cung cấp nội dung (
CalendarProvider) và công cụ cho nhà phát triển theo yêu cầu (Traceur) dành 70% đến 99,9% ảnh chụp nhanh trường ở trạng thái rảnh ở mức ~1 MB đến 3 MB. Việc tính toán phân vị thô trên tất cả ảnh chụp nhanh sẽ làm giảm đáng kể số liệu ước tính P95 và P99. Thay vào đó, phân vị và khoảng tin cậy được tính toán dựa trên ảnh chụp nhanh hoạt động không bị pha loãng (lọc ra ảnh chụp nhanhCACHEDvà ảnh chụp nhanh cơ sở rảnh). - Sử dụng khoảng tin cậy theo cụm thiết bị: Vì một thiết bị đóng góp nhiều ảnh chụp nhanh định kỳ theo thời gian, nên việc coi ảnh chụp nhanh là mẫu độc lập sẽ làm giảm độ không chắc chắn của đuôi. Tất cả các giá trị ước tính tập hợp đang hoạt động P95 và P99 đều được đánh giá bằng cách sử dụng khoảng tin cậy 95% được phân cụm theo thiết bị (sử dụng số lượng thiết bị riêng biệt
N_devlàm kích thước mẫu hiệu quả). - Tính đến các mức tăng đột biến tạm thời bằng
memory.peak: Việc lấy mẫu định kỳ có thể bỏ lỡ các mức tăng đột biến ngắn hạn, có mức phân bổ cao (chẳng hạn nhưNetworkStackxác thực TLS của trang xác thực và các lượt thăm dò DNS đồng thời, hoặcBluetoothkhởi chạy A2DP và BLE). Đối với các dịch vụ có mức sử dụng tăng đột biến, ngân sách được điều chỉnh để bao gồm phân vị thứ 90 đến 95 của mức sử dụng cao nhất của nhânmemory.peaktrong suốt thời gian hoạt động trên các cấp RAM. - Xác nhận rằng một cấp RAM phản ánh RAM, chứ không phải thế hệ thiết bị: Các thiết bị mới hơn có xu hướng có cả nhiều RAM và nhiều tính năng hơn, vì vậy, một tập hợp đang hoạt động tăng lên theo cấp RAM có thể thực sự tăng lên theo tính năng. Trước khi thêm
bậc
maxDeviceRamMb, hãy phân chia dữ liệu theo mẫu thiết bị. Ví dụ:com.android.phoneđạt cùng một P99anon + swap(~85 đến 90 MB) trên điện thoại 12 GB và 16 GB cùng thế hệ, trong khi điện thoại gập 12 GB cũ hơn sử dụng 40 MB. Do đó, ngân sách của nó sử dụng một giá trị duy nhất không có cấp RAM, nhờ vậy mà thiết bị có RAM thấp hơn cung cấp các tính năng điện thoại mới hơn sẽ không bị thiếu ngân sách.
Hình 5. Tập hợp đang hoạt động so với bộ nhớ đệm tệp có thể lấy lại.
CUJ trên thiết bị thực và quét tỷ lệ hiển thị
Để xác thực ngân sách dựa trên dữ liệu đo từ xa và hiệu chỉnh ngân sách theo tỷ lệ hiển thị (chẳng hạn như :PhotoPicker) và ngân sách nền theo trạng thái cụ thể (chẳng hạn như Traceur), các lần quét memory.high trực tiếp đã được thực hiện trên các thiết bị thực có RAM từ 6 GB đến 16 GB:
- Tìm điểm uốn không có lỗi lặp lại: Trong khi thực hiện các CUJ toàn diện (chẳng hạn như lướt nhanh lưới ảnh, duyệt xem album, vuốt xem trước ảnh và video có độ phân giải cao ở chế độ toàn màn hình, cũng như ghi và lưu dấu vết),
memory.highđược giảm dần trong khi đo lường bộ đếm cgroupmemory.stat(workingset_refault_anon,workingset_refault_file,pgscanvàpgsteal) vàmemory.events(high). Như minh hoạ trong hình 6, ngân sách tối ưu nằm ngay trên điểm uốn không có lỗi lặp lại. Điểm uốn không lỗi tham chiếu lại là ngưỡng mà tại đó hạt nhân lấy lại bộ nhớ đệm tệp một lần không hoạt động (pgsteal > 0) mà không kích hoạt lỗi trang liên tục của tập hợp hoạt động (workingset_refault == 0). - Xác thực tỷ lệ pixel hiển thị trên các độ phân giải: Thử nghiệm
:PhotoPickertrên 4 độ phân giải màn hình vật lý và mô phỏng từ ~1,2 MP (720×1600) đến ~4,5 MP (màn hình bên trong mở ra 2076×2152) xác nhận rằng tập hợp hoạt động bitmap Glide và ViewPager2 đang hoạt động mở rộng tuyến tính theo số lượng pixel hiển thị. Đường cơ sở không hiển thị cũng khác nhau tuỳ theo thiết bị: với số lượng pixel gần như giống nhau, điểm uốn không có lỗi tải lại là khoảng 140 MB trên màn hình ngoài của thiết bị gập 16 GB so với 110 đến 125 MB trên điện thoại 6 GB. Mức cơ sở 85 MB cộng với 25 byte cho mỗi pixel hiển thị (android:maxMb="85"vàandroid:additionalBytesPerDisplayPixel="25") luôn cao hơn mức đo được trên mọi màn hình được thử nghiệm (~110 MB trên HD+, ~145 MB trên FHD+ và ~190 MB trên màn hình gập). - Xác minh tính năng dọn rác ART ở trạng thái nền: Trong cgroup v2,
các trang hình ảnh khung và
boot.artdo Zygote chia sẻ vẫn được tính vào cgroup của Zygote và được coi là không gian miễn nhiễm trong quá trình dọn rác đồng thời (CMC) của ART. Thử nghiệm chịu tải xác nhận rằng khi một ứng dụng giao diện người dùng hệ thống chuyển sangbackground, việc thắt chặtmemory.high(ví dụ: thành 30 MB trênTraceur) sẽ giới hạn rõ ràng vùng nhớ khối xếp riêng tư và bộ nhớ đệm trang mà không gây ra tình trạng dọn rác quá mức hoặc hoạt động hoán đổi.
Hình 6. Tìm điểm gãy không có lỗi trang lặp lại trong một hành trình quan trọng của người dùng (CUJ).
Tóm tắt ngân sách ứng dụng hệ thống tham chiếu AOSP
Bảng sau đây tóm tắt ngân sách tham chiếu đã hiệu chỉnh trong AOSP trên các cấp RAM và trạng thái quy trình:
| Gói hoặc quy trình hệ thống | Phạm vi | RAM ≤8 GB | RAM 12 GB | RAM ≥16 GB | Trình điều khiển hiệu chỉnh chính |
|---|---|---|---|---|---|
com.android.bluetooth |
UID (<application>) |
80 MB | 95 MB | 110 MB | Bao gồm các mức tăng đột biến memory.peak trong vòng đời P90 (khoảng 75 đến 110 MB) và memory.current hoạt động P99 gấp hơn 2 lần. |
com.android.nfc |
UID (<application>) |
50 MB | 60 MB | 60 MB | Bao gồm P99 (tối đa ~50 MB) và P90 đến P95 memory.peak của tính năng Giả lập thẻ dựa trên máy chủ đang hoạt động trong khi cắt bớt bộ nhớ đệm tệp nguội. |
com.android.providers.calendar |
UID (<application>) |
70 MB mặc định (nền trước) 50 MB perceptible và background |
Tập hợp đang hoạt động không thay đổi trên các cấp RAM. Ngân sách nền giữ lại khoảng trống ẩn danh cho chuông báo nhắc nhở trong khi cắt bớt bộ nhớ đệm trang SQLite trong quá trình đồng bộ hoá. | ||
com.android.traceur |
UID (<application>) |
85 MB theo mặc định (trên nền trước và có thể nhận thấy) 30 MB background |
Duy trì toàn bộ khoảng trống 85 MB trong quá trình ghi và xuất dấu vết đang hoạt động (TraceService), đồng thời giới hạn trạng thái rảnh và phát sóng ở mức 30 MB. |
||
com.android.providers.media.module (chính) |
PID (<process>) |
60 MB | 75 MB | 85 MB | Cung cấp khoảng trống từ 10 đến 30 MB so với P99 anon + swap trong khi lấy lại bộ nhớ đệm tệp hình thu nhỏ và SQLite không hoạt động. |
com.android.providers.media.module:PhotoPicker |
PID (<process>) |
85 MB + 25 byte/pixel (khoảng 110 MB trên HD+, khoảng 145 MB trên FHD+, khoảng 190 MB trên màn hình gập khoảng 4,5 MP) |
Điều chỉnh tỷ lệ theo số lượng pixel hiển thị (additionalBytesPerDisplayPixel="25") để khớp với điểm uốn của tập hợp hoạt động bitmap Glide. |
||
com.android.networkstack |
UID (<application>) |
65 MB | 70 MB | 85 MB | Chủ yếu là bộ nhớ ẩn danh, có kích thước bằng thời gian hoạt động P95 memory.peak để xử lý các đợt tăng đột biến về TLS, DNS và DHCP của trang xác thực. |
com.android.appsearch.apk |
UID (<application>) |
130 MB | Bao gồm khoảng 100 MB bộ nhớ bị khoá, không thể loại bỏ cộng với một tập hợp hoạt động của máy chủ lưu trữ nóng khoảng 5 MB, có không gian để vùng bị khoá phát triển. Không có lỗi tham chiếu nào được đo lường ở mức ≥125 MB trong khi bộ nhớ đệm trang nguội (lên đến ~330 MB ở P99) được thu hồi. | ||
com.android.phone (TeleService) |
PID (<process>) |
120 MB | Phạm vi theo từng PID trên UID dùng chung 1001. Tập hợp đang hoạt động theo dõi thế hệ thiết bị thay vì RAM, vì vậy, một ngân sách duy nhất sẽ cao hơn khoảng 30 MB so với anon + swap P99 cao nhất trên mỗi kiểu máy. |
||
Cách nhà sản xuất thiết bị gốc (OEM) có thể hiệu chỉnh lại ngân sách ứng dụng hệ thống cho thiết bị của họ
OEM tuỳ chỉnh ứng dụng hệ thống (ví dụ: thêm hồ sơ Bluetooth của nhà cung cấp, tiện ích điện thoại tuỳ chỉnh hoặc trình lập chỉ mục nội dung nghe nhìn của OEM) hoặc cung cấp thêm dịch vụ hệ thống được cài đặt sẵn có thể hiệu chỉnh lại ngân sách hiện có hoặc tạo tệp *-budget.xml mới cho danh mục phần cứng của họ.
Thu thập hồ sơ bộ nhớ cgroup v2 đang hoạt động
Đối với mỗi quy trình hệ thống mục tiêu, hãy đọc các nút bộ nhớ cgroup v2 trong /sys/fs/cgroup/uid_<uid>/pid_<pid>/ trên các khối lượng công việc và cấp RAM tiêu biểu:
memory.current: Bộ nhớ thường trú hiện tại được tính cho cgroup (không bao gồm các trang được chia sẻ Zygote)memory.peak: Mức cao nhấtmemory.currenttrong suốt thời gian hoạt động kể từ khi bắt đầu quy trìnhmemory.swap.current: Mức sử dụng bộ nhớ hoán đổi nén hiện tại trong ZRAMmemory.stat: Thông tin chi tiết vềanon,file,pgscan,pgsteal,workingset_refault_anonvàworkingset_refault_filememory.events: Số lượng tích luỹ các lỗi vi phạmhigh(memory.highsự kiện điều tiết và thu hồi)
Khi tổng hợp dữ liệu đo từ xa từ các thiết bị thử nghiệm nội bộ hoặc thiết bị thực tế, hãy tách riêng các trạng thái quy trình đang hoạt động (TOP, FOREGROUND_SERVICE, liên kết đang hoạt động và đồng bộ hoá đang hoạt động) khỏi trạng thái CACHED rảnh hoặc PERSISTENT rảnh để ảnh chụp nhanh trạng thái rảnh không làm sai lệch các phép tính tập hợp hoạt động P95 và P99 theo hướng giảm xuống.
Tách vùng nhớ khối xếp không thể nén khỏi bộ nhớ đệm tệp có thể thu hồi
So sánh phân vị thứ 99 của anon + swap với phân vị thứ 99 của memory.current:
- Dịch vụ do bộ nhớ đệm tệp chi phối (
file≫anon, chẳng hạn như nhà cung cấp cơ sở dữ liệu, trình lập chỉ mục của Tìm kiếm và trình nền nhật ký): Đặtandroid:maxMbđể cung cấp khoảng trống thoải mái (thường là 1, 5 lần đến 4 lần) so với P99anon + swapcộng với cửa sổ làm việc cho các trang tệp nóng. Cấu hình này cho phép hạt nhân liên tục loại bỏ các trang tệp chưa được liên kết mà không cần chạm vào vùng nhớ khối xếp ẩn danh. Nếuunevictablehoặcfile_mappedlớn (như với bộ nhớ bị khoá hoặc cơ sở dữ liệu được liên kết với bộ nhớ), hãy tính cả mức sử dụng đó vào tập hợp đang hoạt động. - Dịch vụ có nhiều vùng nhớ khối xếp ẩn danh hoặc dịch vụ đột biến (
anonchiếm phần lớnmemory.current, chẳng hạn như ngăn xếp mạng hoặc ngăn xếp kết nối): Đặt kích thướcandroid:maxMbđể bao gồm P90 đến P95 củamemory.peakđể các mức tăng đột biến tạm thời không kích hoạt tính năng hoán đổi ZRAM đồng bộ trên các luồng nhạy cảm với độ trễ. - Quy trình giao diện người dùng cần nhiều màn hình (chẳng hạn như bộ chọn, thư viện hoặc trình trợ giúp giao diện người dùng hệ thống): Đo lường mức sử dụng bộ nhớ cơ sở không phải giao diện người dùng và mức sử dụng bộ nhớ bitmap gia tăng trên mỗi megapixel trên màn hình có độ phân giải thấp nhất và cao nhất, đồng thời thể hiện ngân sách bằng
android:maxMbcộng vớiandroid:additionalBytesPerDisplayPixel.
Xoá memory.high trên thiết bị thực
Trước khi cam kết ngân sách mới cho bản dựng, hãy sử dụng am memory-limiter manual (hoặc ghi trực tiếp vào nút cgroup memory.high của quy trình trên bản dựng userdebug) để kiểm thử các giới hạn đề xuất trong khi chạy kiểm thử CUJ tự động:
# Override memory.high for a specific PID to 60 MB during a CUJ test
adb shell am memory-limiter manual <PID> 60MBTheo dõi memory.stat và memory.events trước và sau CUJ:
- Ngân sách quá thấp:
workingset_refault_anonhoặcworkingset_refault_filetăng đột biến trong CUJ,memory.swap.currenttăng liên tục trong quá trình hoạt động ở nền trước, hoặc Độ trễ của khung hình CUJ hoặc Binder bị hồi quy. Tăng ngân sách vượt quá mức mặc định. - Ngân sách quá cao:
memory.events:highvẫn là0ngay cả sau khi thực hiện các khối lượng công việc lập chỉ mục hoặc truyền trực tuyến tệp nặng, vàfiletrongmemory.stattăng lên không giới hạn với các trang nguội. Thắt chặt ngân sách cho tập hợp đang hoạt động. - Ngân sách được điều chỉnh phù hợp:
memory.currentở trạng thái ổn định luôn ở mức dướimemory.hightrong quá trình hoạt động bình thường. Trong thời gian tệp hoặc bộ nhớ đệm tăng đột biến,pgstealsẽ lấy lại các trang tệp ít dùng trong khiworkingset_refault_anonvẫn gần bằng 0 và độ trễ CUJ không thay đổi.