Tối ưu hoá bộ nhớ là yếu tố quan trọng để mang lại trải nghiệm chơi trò chơi ổn định, hiệu suất cao trên Android. Hướng dẫn này cung cấp thông tin tổng quan về lý do hiệu suất bộ nhớ lại quan trọng, cách hệ điều hành Android quản lý giới hạn bộ nhớ của quy trình và các chỉ số bộ nhớ mới trong Google Play Console để giúp bạn theo dõi và cải thiện chất lượng kỹ thuật của trò chơi.
Tầm quan trọng của việc tối ưu hoá bộ nhớ
Việc tối ưu hoá bộ nhớ của trò chơi là điều cần thiết để duy trì tỷ lệ giữ chân người chơi, mở rộng khả năng tương thích với thiết bị và tuân thủ các tiêu chuẩn chất lượng của nền tảng:
- Ngăn chặn khởi động nguội (trải nghiệm và giữ chân người dùng): Khi người chơi tạm thời chuyển khỏi trò chơi của bạn (ví dụ: để trả lời thông báo hoặc kiểm tra tin nhắn), hệ điều hành sẽ đặt quy trình trò chơi vào chế độ nền. Nếu mức sử dụng bộ nhớ nền của trò chơi quá cao, thì Trình loại bỏ ứng dụng khi bộ nhớ thấp (LMK) của hệ thống sẽ ưu tiên chấm dứt quy trình trò chơi để lấy lại RAM cho các tác vụ trên nền trước. Lần tiếp theo người dùng tiếp tục, thay vì tiếp tục ấm liền mạch và tức thì, trò chơi phải trải qua một quá trình khởi động nguội dài – tải lại hoàn toàn các thành phần đồ hoạ, âm thanh và tệp nhị phân của công cụ phát triển trò chơi từ bộ nhớ. Việc duy trì mức sử dụng bộ nhớ ở chế độ nền thấp sẽ ngăn chặn các hoạt động chấm dứt ngầm ở chế độ nền, duy trì trạng thái người dùng và đảm bảo người chơi có thể tiếp tục phiên của họ ngay lập tức. Để biết thêm thông tin chi tiết về hành vi LMK của hệ thống, hãy xem hướng dẫn Android vitals – Trình loại bỏ ứng dụng khi bộ nhớ thấp.
- Tính ổn định của hệ sinh thái và thiết bị: Mức sử dụng bộ nhớ không hiệu quả và rò rỉ bộ nhớ sẽ làm giảm hiệu suất tổng thể của hệ thống. Khi bộ nhớ hệ thống khan hiếm, hệ thống sẽ chịu áp lực nghiêm trọng, dẫn đến tốc độ khung hình giảm, giao diện người dùng bị giật và âm thanh bị lỗi. Nếu áp lực bộ nhớ quá nghiêm trọng, Trình tắt ứng dụng khi bộ nhớ thấp (LMK) của hệ thống sẽ buộc phải chấm dứt các quy trình trong nền, khiến các ứng dụng khác phải khởi động nguội chậm và mất trạng thái người dùng khi người chơi chuyển đổi giữa các tác vụ.
- Chấm dứt ở cấp nền tảng: Kể từ Android 17 (cấp độ API 37), hệ thống sẽ chủ động hơn trong việc chấm dứt các quy trình sử dụng quá nhiều bộ nhớ. Nếu mức sử dụng bộ nhớ của trò chơi quá cao, hệ điều hành có thể chấm dứt đột ngột quy trình của trò chơi mà không tạo ra dấu vết ngăn xếp chuẩn.
- Khả năng tương thích của thiết bị: Mặc dù các thiết bị cao cấp có RAM từ 12 GB đến 16 GB, nhưng phần lớn người chơi trò chơi trên toàn cầu sử dụng thiết bị có RAM 4 GB hoặc 6 GB. Việc quản lý bộ nhớ đúng cách giúp đảm bảo trò chơi của bạn luôn có thể truy cập và phản hồi nhanh trên tất cả các cấp phần cứng mà không cần các gói tài sản riêng biệt, phức tạp.
Tìm hiểu về bộ nhớ trong Android
Để thiết kế chiến lược lập ngân sách bộ nhớ hiệu quả, nhà phát triển phải hiểu cách nền tảng Android quản lý bộ nhớ thực và cách nền tảng này đo lường mức sử dụng bộ nhớ đang hoạt động của trò chơi.
Các khái niệm chính về bộ nhớ Android
Để biết các khái niệm cơ bản về quản lý bộ nhớ ở cấp nền tảng, hãy xem tài liệu chính thức về Tổng quan về quản lý bộ nhớ. Tài nguyên này bao gồm bốn lĩnh vực kiến trúc:
- Tổng quan về bộ nhớ: Android sử dụng tính năng phân trang và ánh xạ bộ nhớ (mmap) để quản lý RAM. Android không hỗ trợ tệp hoán đổi truyền thống trên ổ đĩa; thay vào đó, Android dựa vào tính năng nén trang (sử dụng zRAM) và thu hồi trang để giải phóng bộ nhớ vật lý.
- Phân bổ bộ nhớ giữa các quy trình: Android chia sẻ RAM trên toàn hệ thống. Hệ thống này chỉ định các vùng nhớ khối xếp cụ thể cho việc thực thi máy ảo Dalvik hoặc ART, đồng thời cho phép các môi trường phát triển gốc (chẳng hạn như công cụ phát triển trò chơi C++) yêu cầu bộ nhớ từ vùng nhớ khối xếp hệ thống gốc.
- Quản lý bộ nhớ ứng dụng: Hoạt động theo mô hình đa quy trình, Android yêu cầu các ứng dụng theo dõi trạng thái vòng đời của chúng một cách linh động và tự nguyện giải phóng các tài nguyên không cần thiết (chẳng hạn như đồ hoạ và bitmap không được lưu vào bộ nhớ đệm) để hỗ trợ tình trạng hệ thống.
- Tổng quan về quy trình và luồng: Hệ thống phân loại các quy trình thành một hệ phân cấp dựa trên mức độ hiển thị và tầm quan trọng hiện tại mà người dùng nhận thấy, xác định quy trình nào được duy trì và quy trình nào bị chấm dứt trước trong điều kiện bộ nhớ thấp.
Chỉ số tổng mức sử dụng bộ nhớ
Trình giới hạn bộ nhớ Android 17 ở cấp nền tảng đánh giá mức tiêu thụ của quy trình bằng cách sử dụng Tổng mức sử dụng bộ nhớ thay vì tổng kích thước thường trú (RSS) hoặc kích thước bộ nhớ ảo.
Tổng mức sử dụng bộ nhớ = RSS ẩn danh (RssAnon) + Bộ nhớ hoán đổi không nén (VmSwap)
Để ngăn trò chơi vượt quá giới hạn của nền tảng, bạn phải hiểu chính xác những chỉ số này thể hiện điều gì ở cấp hệ thống. Để biết thêm thông tin về các chỉ số này, mức phân bổ RAM vật lý và cách xử lý các trang được sao lưu bằng tệp, hãy xem phần Tìm hiểu về chỉ số RSS và chỉ số hoán đổi trong hướng dẫn Theo dõi mức sử dụng bộ nhớ.
Các quy tắc ràng buộc về bộ nhớ
Để duy trì độ ổn định của hệ thống và đảm bảo rằng các ứng dụng không tiêu tốn quá nhiều tài nguyên, nền tảng Android quản lý giới hạn bộ nhớ cho các quy trình đang chạy.
Trình giới hạn bộ nhớ trong Android 17 trở lên
Android 17 (cấp độ API 37) trở lên quản lý giới hạn bộ nhớ nghiêm ngặt cho mỗi ứng dụng bằng cách sử dụng Linux cgroup phiên bản 2 để ngăn từng ứng dụng gây ra tình trạng không ổn định trên toàn hệ thống. Để biết thêm thông tin chi tiết về cách triển khai kỹ thuật, hãy xem Hướng dẫn về Trình giới hạn bộ nhớ AOSP và bài đăng trên blog Ưu tiên hiệu suất bộ nhớ: Các bước thiết yếu cho Android 17.
- Cơ chế: Trình giới hạn bộ nhớ giám sát tất cả quy trình của ứng dụng và chỉ định giới hạn một cách linh động dựa trên trạng thái vòng đời của quy trình:
- Quy trình hiển thị (nền trước): Các quy trình ứng dụng hiện đang hiển thị giao diện người dùng dự kiến sẽ chạy một tập hợp tài nguyên lớn hơn và được cung cấp hạn mức cao hơn.
- Quy trình không hiển thị (nền hoặc dịch vụ): Các quy trình ứng dụng đang hoạt động mà không hiển thị giao diện người dùng sẽ bị ràng buộc vào một ngân sách chặt chẽ hơn, hạn chế hơn.
- Thuộc tính hạt nhân: Dịch vụ này dựa vào hai thuộc tính chính:
memory.high: Giới hạn mềm. Khi vượt quá, kernel sẽ điều tiết quy trình và cố gắng thu hồi bộ nhớ một cách quyết liệt. Việc thu hồi này có thể khiến trò chơi bị giảm hiệu suất.memory.swap.max: Quản lý giới hạn cứng đối với không gian hoán đổi hoặc zRAM mà quy trình có thể sử dụng.
- Hành vi chấm dứt: Nếu một quy trình tiếp tục phân bổ bộ nhớ ẩn danh vượt quá
memory.highvà làm cạn kiệt dung lượng hoán đổi, thì quá trình phân bổ sẽ không thành công và hệ điều hành sẽ tự động tắt quy trình đó. Việc chấm dứt này được ghi lại bằngApplicationExitInfotheo lý do thoát Trình giới hạn bộ nhớ (bắt đầu từ Android 17, 26Q4).
Giới hạn bộ nhớ mới trong Android vitals trên Play Console
Để giúp nhà phát triển chủ động xác định các vấn đề về bộ nhớ, Google Play sẽ ra mắt các chỉ số mới trong Android vitals của Play Console. Play Console theo dõi mức sử dụng bộ nhớ RSS ẩn danh + Bộ nhớ hoán đổi ở phân vị thứ 90 (P90) trong các phiên chơi trò chơi để xác định các giá trị ngoại lai cực đoan.
Ngưỡng cảnh báo và ngưỡng thực thi được điều chỉnh theo dung lượng RAM vật lý của thiết bị và trạng thái của quy trình. Các giới hạn này được áp dụng theo hai giai đoạn riêng biệt.
Để biết hướng dẫn chi tiết và giới hạn bộ nhớ, hãy xem phần Android vitals – Ngưỡng hành vi xấu là gì?.
Dịch vụ mà người dùng nhận thấy
Dịch vụ có thể nhận biết là các tiến trình nền quan trọng mà hệ thống Android cho rằng người dùng có thể nhận thấy. Trạng thái này bao gồm tất cả các quy trình đang chạy:
- Dịch vụ nền trước (FGS)
- Công việc ưu tiên
- Công việc chuyển dữ liệu do người dùng khởi tạo
- Dịch vụ ràng buộc với hệ thống hoặc dịch vụ ràng buộc với các ứng dụng khác
Vì Perceptible Services được thiết kế cho các tác vụ quan trọng, chạy trong thời gian dài ở chế độ nền, nên các dịch vụ này rất dễ bị rò rỉ vòng đời tích luỹ. Giới hạn bộ nhớ của nền tảng Android coi trạng thái này là không phải trên nền trước, nghĩa là nếu trò chơi của bạn chạy ở chế độ nền nhưng vẫn tiếp tục chạy một Dịch vụ mà người dùng nhận biết được, thì trò chơi đó sẽ phải tuân theo giới hạn bộ nhớ nghiêm ngặt hơn cho dịch vụ hoặc chế độ nền, như được trình bày trên trang Trung tâm trợ giúp của Play. Trò chơi phải cắt giảm mạnh các thành phần không cần thiết khi chuyển từ nền trước sang trạng thái Dịch vụ có thể nhận biết ở chế độ nền.
Yêu cầu về R8
Để giảm thiểu kích thước mã byte và giảm mức hao tổn của quy trình Java cơ bản, Google Play Console đánh giá việc tối ưu hoá mã theo nguyên tắc về chất lượng ứng dụng. Để biết thêm thông tin về các yêu cầu và giới hạn tối ưu hoá áp dụng cho trò chơi, hãy xem phần Tối ưu hoá mã DEX trong tài liệu về Android vitals.
Để định cấu hình R8 trong một dự án Android tiêu chuẩn, hãy làm theo hướng dẫn Bật tính năng tối ưu hoá ứng dụng. Để bật chế độ cài đặt tối ưu hoá và rút gọn nâng cao, hãy xem phần Sử dụng R8 ở chế độ đầy đủ. Để xác định quy tắc nào đang ngăn R8 làm rối mã nguồn các lớp hoặc xoá mã chết, hãy sử dụng Trình phân tích cấu hình R8.
Nếu bạn đang xây dựng bằng một công cụ phát triển trò chơi, thì hành vi mặc định của R8 sẽ thay đổi tuỳ theo công cụ và phiên bản:
- Unity: Tính năng rút gọn mã bị tắt theo mặc định, vì vậy, bạn phải bật tính năng này trong phần cài đặt dự án. Để biết thêm thông tin về các bước hỗ trợ và định cấu hình phiên bản, hãy xem bài viết Rút gọn và tối ưu hoá mã Unity bằng R8.
- Unreal Engine: Kể từ UE 4.25, R8 được bật theo mặc định trong các bản phát hành, yêu cầu bạn quản lý các quy tắc giữ lại cho cầu nối JNI và trình bổ trợ. Để biết thêm thông tin về chi tiết phiên bản và cấu hình UPL, hãy xem bài viết Rút gọn và tối ưu hoá mã Unreal Engine bằng R8.
Yêu cầu về bitmap
Bitmap chiếm một phần lớn mức sử dụng bộ nhớ trong các trò chơi có độ chân thực cao hiện đại. Vì dữ liệu pixel Bitmap được lưu trữ trực tiếp trong vùng nhớ khối xếp gốc không được quản lý trên Android 8.0 (cấp độ API 26) trở lên, nên việc tải hình ảnh chưa tối ưu hoá có thể đẩy các quy trình vượt quá ngưỡng bộ nhớ của nền tảng. Để biết các phương pháp hay nhất về việc điều chỉnh tỷ lệ và lưu hình ảnh vào bộ nhớ đệm, hãy xem bài viết Tối ưu hoá việc sử dụng hình ảnh.
Giám sát mức sử dụng bộ nhớ
Để tối ưu hoá hiệu quả bộ nhớ của trò chơi, trước tiên, bạn phải hiểu cách nền tảng Android đo lường mức sử dụng bộ nhớ. Android 17 cập nhật chỉ số bộ nhớ để theo dõi tổng của RSS ẩn danh (RssAnon) và Bộ nhớ hoán đổi chưa nén (VmSwap), không bao gồm bộ nhớ được hỗ trợ bằng tệp hoặc bộ nhớ riêng của GPU. Hướng dẫn này trình bày chi tiết cách sử dụng các công cụ cấp hệ thống như Perfetto và meminfo, triển khai các API chẩn đoán như ProfilingManager và onTrimMemory, cũng như trích xuất thông tin phân bổ bộ nhớ chính xác trong Unity và Unreal Engine. Tìm hiểu cách lập hồ sơ chính xác cho trò chơi và tránh tình trạng giật hiệu suất liên quan đến việc thăm dò bộ nhớ thời gian chạy truyền thống.
Để biết thêm thông tin, hãy xem phần Theo dõi mức sử dụng bộ nhớ.
Chiến lược giảm mức sử dụng bộ nhớ
Mặc dù công cụ phát triển trò chơi giúp đơn giản hoá quá trình phát triển trên nhiều nền tảng, nhưng việc xử lý bộ nhớ mặc định của các công cụ này có thể kích hoạt giới hạn bộ nhớ ở cấp hệ điều hành. Trang này trình bày chi tiết các bước tối ưu hoá thực tế dành riêng cho Unity và Unreal Engine. Tìm hiểu lý do tại sao việc dựa vào onTrimMemory dựa trên Java có thể gây ra tình trạng tắc nghẽn trong Unity và cách sử dụng phương thức gọi lại trong vòng đời gốc. Bạn cũng sẽ khám phá các tính năng tối ưu hoá quan trọng ở cấp tài sản, chẳng hạn như sử dụng tính năng nén kết cấu ASTC 8x8 và định cấu hình việc huỷ tải tài sản, để trò chơi của bạn luôn chạy mượt mà trên mọi cấp phần cứng.
Để biết thêm thông tin, hãy xem phần Giảm mức sử dụng bộ nhớ.