Các phương pháp hay nhất để quản lý bộ nhớ

Tài liệu này giả định rằng bạn đã làm theo hướng dẫn về phương pháp hay nhất dành cho các ứng dụng Android trong phần quản lý bộ nhớ, chẳng hạn như Quản lý bộ nhớ của ứng dụng.

Giới thiệu

Rò rỉ bộ nhớ là một loại rò rỉ tài nguyên xảy ra khi một chương trình máy tính không giải phóng bộ nhớ được phân bổ không còn cần thiết nữa. Sự cố rò rỉ có thể dẫn đến việc ứng dụng yêu cầu nhiều bộ nhớ hơn từ hệ điều hành so với mức dung lượng có sẵn, từ đó gây ra sự cố cho ứng dụng. Một số phương pháp không đúng có thể gây rò rỉ bộ nhớ trong ứng dụng Android, chẳng hạn như không huỷ bỏ đúng cách tài nguyên hoặc không huỷ đăng ký trình nghe khi không còn cần thiết nữa.

Tài liệu này cung cấp cho bạn một số phương pháp hay nhất để giúp ngăn ngừa, phát hiện và giải quyết các trường hợp rò rỉ bộ nhớ trong mã của bạn. Nếu bạn đã thử các phương thức trong tài liệu này và nghi ngờ rằng SDK của chúng tôi đã bị rò rỉ bộ nhớ, hãy xem phần Cách báo cáo vấn đề về SDK của Google.

Trước khi bạn liên hệ với nhóm hỗ trợ

Trước khi bạn báo cáo sự cố rò rỉ bộ nhớ cho nhóm hỗ trợ Google, hãy làm theo các phương pháp hay nhất cùng với các bước gỡ lỗi trong tài liệu này để đảm bảo lỗi không có trong mã của bạn. Các bước này có thể giúp giải quyết vấn đề của bạn. Nếu không, chúng sẽ tạo ra thông tin mà nhóm hỗ trợ Google cần để giúp bạn.

Tránh rò rỉ bộ nhớ

Hãy làm theo các phương pháp hay nhất sau để tránh một số nguyên nhân phổ biến nhất gây ra sự cố rò rỉ bộ nhớ trong mã sử dụng SDK của Google.

Các phương pháp hay nhất dành cho ứng dụng Android

Kiểm tra xem bạn đã thực hiện tất cả các thao tác sau trong ứng dụng Android của mình hay chưa:

  1. Giải phóng các tài nguyên không sử dụng.
  2. Huỷ đăng ký trình nghe khi không cần thiết nữa.
  3. Huỷ việc cần làm khi không cần thiết.
  4. Chuyển tiếp các phương thức vòng đời để giải phóng tài nguyên.
  5. Sử dụng phiên bản SDK mới nhất

Để biết thông tin cụ thể về từng phương pháp này, hãy xem các phần sau đây.

Huỷ bỏ các tài nguyên không dùng đến

Khi ứng dụng Android của bạn sử dụng một tài nguyên, hãy nhớ giải phóng tài nguyên đó khi không còn cần đến. Nếu không, tài nguyên sẽ tiếp tục chiếm bộ nhớ ngay cả sau khi ứng dụng xử lý xong các tài nguyên đó. Để biết thêm thông tin, hãy xem phần Vòng đời hoạt động trong tài liệu Android.

Phát hành các mục tham chiếu GoogleMap cũ trong GeoSDKs

Một lỗi phổ biến là GoogleMap có thể gây rò rỉ bộ nhớ nếu được lưu vào bộ nhớ đệm bằng NavigationView hoặc MapView. GoogleMap có mối quan hệ 1:1 với NavigationView hoặc MapView mà từ đó nó được truy xuất. Bạn phải đảm bảo rằng GoogleMap không được lưu vào bộ nhớ đệm hoặc tệp tham chiếu được phát hành khi NavigationView#onDestroy hoặc MapView#onDestroy được gọi. Nếu sử dụng NavigationSupportFragment, MapSupportFragment hoặc mảnh của riêng bạn bao bọc những khung hiển thị này, thì bạn phải phát hành tham chiếu trong Fragment#onDestroyView.

class NavFragment : SupportNavigationFragment() {

  var googleMap: GoogleMap?

  override fun onCreateView(
    inflater: LayoutInflater,
    parent: ViewGroup?,
    savedInstanceState: Bundle?,
  ): View  {
    super.onCreateView(inflater,parent,savedInstanceState)
    getMapAsync{map -> googleMap = map}
  }

  override fun onDestroyView() {
    googleMap = null
  }
}

Huỷ đăng ký trình nghe khi không cần nữa

Khi ứng dụng Android của bạn đăng ký trình nghe cho một sự kiện, chẳng hạn như lượt nhấp vào nút hoặc thay đổi về trạng thái của khung hiển thị, hãy nhớ huỷ đăng ký trình nghe khi ứng dụng không cần theo dõi sự kiện nữa. Nếu không, trình nghe sẽ tiếp tục chiếm bộ nhớ ngay cả sau khi ứng dụng hoàn tất quá trình xử lý.

Ví dụ: giả sử ứng dụng của bạn dùng SDK Điều hướng và gọi trình nghe sau để theo dõi các sự kiện đến: phương thức addArrivalListener để theo dõi các sự kiện đến, ứng dụng cũng nên gọi removeArrivalListener khi không cần theo dõi các sự kiện đến nữa.

var arrivalListener: Navigator.ArrivalListener? = null

fun registerNavigationListeners() {
  arrivalListener =
    Navigator.ArrivalListener {
      ...
    }
  navigator.addArrivalListener(arrivalListener)
}

override fun onDestroy() {
  navView.onDestroy()
  if (arrivalListener != null) {
    navigator.removeArrivalListener(arrivalListener)
  }

  ...
  super.onDestroy()
}

Huỷ việc cần làm khi không cần thiết

Khi một ứng dụng Android bắt đầu một tác vụ không đồng bộ, chẳng hạn như tải xuống hoặc yêu cầu mạng, hãy nhớ huỷ tác vụ đó khi nó hoàn tất. Nếu không huỷ được tác vụ, tác vụ sẽ tiếp tục chạy trong nền ngay cả sau khi ứng dụng đã hoàn tất.

Để biết thêm thông tin về các phương pháp hay nhất, hãy xem phần Quản lý bộ nhớ của ứng dụng trong tài liệu của Android.

Chuyển tiếp các phương thức vòng đời để giải phóng tài nguyên

Nếu ứng dụng của bạn dùng SDK Điều hướng hoặc SDK Maps, hãy nhớ phát hành các tài nguyên bằng cách chuyển tiếp các phương thức vòng đời (như hình in đậm) đến navView. Bạn có thể thực hiện việc này bằng cách sử dụng NavigationView trong SDK điều hướng hoặc MapView trong SDK của Maps hoặc SDK chỉ đường. Bạn cũng có thể sử dụng SupportNavigationFragment hoặc SupportMapFragment thay vì sử dụng trực tiếp NavigationViewMapView. Các mảnh hỗ trợ xử lý việc chuyển tiếp các phương thức vòng đời.

class NavViewActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    navView = ...
    navView.onCreate(savedInstanceState)
    ...
  }

  override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)
    navView.onSaveInstanceState(savedInstanceState)
  }

  override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)
    navView.onTrimMemory(level)
  }

  /* Same with
    override fun onStart()
    override fun onResume()
    override fun onPause()
    override fun onConfigurationChanged(...)
    override fun onStop()
    override fun onDestroy()
  */
}

Sử dụng phiên bản SDK mới nhất

Các SDK của Google liên tục được cập nhật để bổ sung các tính năng mới, bản sửa lỗi và điểm cải thiện hiệu suất. Hãy luôn cập nhật SDK trong ứng dụng của bạn để nhận được các bản sửa lỗi này.

Gỡ lỗi rò rỉ bộ nhớ

Nếu bạn vẫn thấy bộ nhớ bị rò rỉ sau khi triển khai tất cả các đề xuất phù hợp trước đó trong tài liệu này, hãy làm theo quy trình bên dưới để gỡ lỗi.

Trước khi bắt đầu, bạn nên làm quen với cách Android quản lý bộ nhớ. Để biết thông tin, hãy đọc bài viết Tổng quan về việc quản lý bộ nhớ trên Android.

Để khắc phục lỗi rò rỉ bộ nhớ, hãy làm theo quy trình sau:

  1. Tái tạo vấn đề. Đây là bước cần thiết để gỡ lỗi mã.
  2. Kiểm tra xem mức sử dụng bộ nhớ dự kiến có được áp dụng hay không. Hãy kiểm tra để đảm bảo rằng mức sử dụng gia tăng có vẻ là rò rỉ không thực sự là dung lượng bộ nhớ cần thiết để chạy ứng dụng.
  3. Gỡ lỗi ở cấp cao. Bạn có thể dùng một số tiện ích để gỡ lỗi. 3 bộ công cụ chuẩn khác nhau giúp gỡ lỗi các vấn đề về bộ nhớ trong Android: Android Studio, Perfetto và tiện ích dòng lệnh Cầu gỡ lỗi Android (adb).
  4. Kiểm tra mức sử dụng bộ nhớ của ứng dụng. Lấy tệp báo lỗi và theo dõi hoạt động phân bổ, sau đó phân tích.
  5. Khắc phục tình trạng rò rỉ bộ nhớ.

Các phần sau đây sẽ trình bày chi tiết những bước này.

Bước 1: Tái tạo vấn đề

Nếu không thể tạo lại vấn đề, trước tiên, hãy xem xét các tình huống có thể dẫn đến rò rỉ bộ nhớ. Việc chuyển thẳng vào việc xem tệp báo lỗi có thể có tác dụng nếu bạn biết rằng vấn đề đã được tạo lại. Tuy nhiên, nếu chỉ nhận được tệp báo lỗi khi khởi động ứng dụng hoặc một thời điểm ngẫu nhiên khác, thì có thể bạn đã không kích hoạt các điều kiện để kích hoạt sự cố rò rỉ. Hãy cân nhắc xử lý nhiều tình huống khi cố gắng tái tạo vấn đề:

  • Bộ tính năng nào được kích hoạt?

  • Trình tự hành động cụ thể nào của người dùng kích hoạt sự cố rò rỉ?

    • Bạn đã thử nhiều lần kích hoạt trình tự này chưa?
  • Ứng dụng đã chuyển qua những trạng thái nào của vòng đời?

    • Bạn đã thử nhiều vòng lặp qua các trạng thái vòng đời khác nhau chưa?

Hãy đảm bảo rằng bạn có thể tái tạo vấn đề trong phiên bản SDK mới nhất. Vấn đề từ phiên bản trước có thể đã được khắc phục.

Bước 2: Kiểm tra xem mức sử dụng bộ nhớ cho ứng dụng có như dự kiến hay không

Mỗi tính năng đều yêu cầu thêm bộ nhớ. Khi bạn gỡ lỗi cho nhiều tình huống, hãy cân nhắc xem đây có phải là việc sử dụng dự kiến hay không hoặc liệu đây có thực sự là một sự cố rò rỉ bộ nhớ hay không. Ví dụ: đối với nhiều tính năng hoặc tác vụ của người dùng, hãy xem xét những khả năng sau:

  • Có thể bị rò rỉ: Việc kích hoạt tình huống này thông qua nhiều vòng lặp sẽ dẫn đến mức sử dụng bộ nhớ tăng lên theo thời gian.

  • Mức sử dụng bộ nhớ dự kiến có thể xảy ra: Bộ nhớ được thu hồi sau khi tình huống ngừng hoạt động.

  • Mức sử dụng bộ nhớ dự kiến có thể xảy ra: Mức sử dụng bộ nhớ tăng trong một khoảng thời gian rồi giảm dần. Điều này có thể là do bộ nhớ đệm bị ràng buộc hoặc mức sử dụng bộ nhớ dự kiến khác.

Nếu hành vi của ứng dụng có thể là mức sử dụng bộ nhớ dự kiến, thì bạn có thể giải quyết vấn đề này bằng cách quản lý bộ nhớ của ứng dụng. Để được trợ giúp, hãy xem phần Quản lý bộ nhớ của ứng dụng.

Bước 3: Gỡ lỗi ở cấp cao

Khi bạn gỡ lỗi rò rỉ bộ nhớ, hãy bắt đầu ở cấp độ cao, sau đó xem chi tiết khi bạn đã thu hẹp khả năng. Hãy sử dụng một trong các công cụ gỡ lỗi cấp cao sau đây để phân tích trước xem có rò rỉ theo thời gian hay không:

Trình phân tích bộ nhớ của Android Studio

Công cụ này cung cấp cho bạn biểu đồ trực quan của bộ nhớ đã sử dụng. Bạn cũng có thể kích hoạt tệp báo lỗi và tính năng theo dõi phân bổ từ cùng giao diện này. Công cụ này là đề xuất mặc định. Để biết thêm thông tin, hãy xem bài viết Trình phân tích bộ nhớ của Android Studio.

Bộ đếm bộ nhớ Perfetto

Perfetto cho phép bạn kiểm soát chính xác việc theo dõi một số chỉ số và trình bày tất cả trong một biểu đồ duy nhất. Để biết thêm thông tin, hãy xem bài viết Bộ đếm bộ nhớ Perfetto.

Giao diện người dùng Perfetto

Tiện ích dòng lệnh cầu gỡ lỗi Android (adb)

Hầu hết những nội dung bạn có thể theo dõi bằng Perfetto cũng có sẵn dưới dạng tiện ích dòng lệnh adb mà bạn có thể truy vấn trực tiếp. Sau đây là một số ví dụ quan trọng:

  • Meminfo cho phép bạn xem thông tin chi tiết về bộ nhớ tại một thời điểm.

  • Procstats cung cấp một số số liệu thống kê tổng hợp quan trọng theo thời gian.

Một số liệu thống kê quan trọng cần xem xét ở đây là mức sử dụng bộ nhớ thực tế tối đa (maxRSS) mà ứng dụng đòi hỏi theo thời gian. MaxPSS có thể không chính xác. Để biết cách tăng độ chính xác, hãy xem cờ adb shell dumpsys procstats --help –start-testing.

Theo dõi phân bổ

Tính năng theo dõi phân bổ xác định dấu vết ngăn xếp nơi bộ nhớ được phân bổ và nếu bộ nhớ chưa được giải phóng. Bước này đặc biệt hữu ích khi theo dõi các lỗi rò rỉ trong mã gốc. Vì công cụ này xác định dấu vết ngăn xếp, nên công cụ này có thể là một phương tiện hữu ích để nhanh chóng gỡ lỗi nguyên nhân gốc hoặc tìm cách tái tạo vấn đề. Để biết các bước sử dụng tính năng theo dõi phân bổ, hãy xem bài viết Gỡ lỗi bộ nhớ trong mã gốc bằng tính năng theo dõi phân bổ.

Bước 4: Kiểm tra mức sử dụng bộ nhớ của ứng dụng bằng tệp báo lỗi

Một cách để phát hiện sự cố rò rỉ bộ nhớ là lấy tệp báo lỗi của ứng dụng, sau đó kiểm tra xem ứng dụng có rò rỉ hay không. Tệp báo lỗi (heap dump) là ảnh chụp nhanh về tất cả các đối tượng trong bộ nhớ của ứng dụng. Bạn có thể dùng công cụ này để chẩn đoán rò rỉ bộ nhớ và các vấn đề khác liên quan đến bộ nhớ.

Android Studio có thể phát hiện các trường hợp rò rỉ bộ nhớ mà GC không thể khắc phục. Khi bạn ghi tệp báo lỗi, Android Studio sẽ kiểm tra xem có hoạt động hoặc mảnh nào vẫn truy cập được nhưng đã bị huỷ hay chưa.

  1. Ghi lại tệp báo lỗi.
  2. Phân tích tệp báo lỗi để tìm lỗi rò rỉ bộ nhớ.
  3. Khắc phục lỗi rò rỉ bộ nhớ.

Để biết chi tiết, hãy xem các phần sau đây.

Ghi tệp báo lỗi

Để ghi tệp báo lỗi, bạn có thể sử dụng Cầu gỡ lỗi Android (adb) hoặc Trình phân tích bộ nhớ trong Android Studio.

Sử dụng adb để ghi tệp báo lỗi

Để ghi tệp báo lỗi bằng adb, hãy làm theo các bước sau:

  1. Kết nối thiết bị Android với máy tính.
  2. Mở lời nhắc lệnh và chuyển đến thư mục chứa công cụ adb.
  3. Để ghi tệp báo lỗi, hãy chạy lệnh sau :

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. Để truy xuất tệp báo lỗi, hãy chạy lệnh sau:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

Sử dụng Android Studio để ghi tệp báo lỗi

Để ghi lại tệp báo lỗi bằng Trình phân tích bộ nhớ của Android Studio, hãy làm theo các bước sau trong phần Ghi lại tệp báo lỗi của Android.

Phân tích tệp báo lỗi để tìm lỗi rò rỉ bộ nhớ

Sau khi ghi lại tệp báo lỗi, bạn có thể sử dụng Trình phân tích bộ nhớ của Android Studio để phân tích. Để thực hiện việc này, hãy làm theo các bước sau:

  1. Mở dự án Android trong Android Studio.

  2. Chọn Run (Chạy), sau đó chọn cấu hình Debug (Gỡ lỗi).

  3. Mở thẻ Trình phân tích tài nguyên Android.

  4. Chọn Memory (Bộ nhớ).

  5. Chọn Open heap dump (Mở tệp báo lỗi) rồi chọn tệp báo lỗi mà bạn đã tạo. Trình phân tích bộ nhớ hiển thị biểu đồ về mức sử dụng bộ nhớ của ứng dụng.

  6. Sử dụng biểu đồ để phân tích tệp báo lỗi:

    • Xác định những đối tượng không còn được sử dụng nữa.

    • Xác định các đối tượng sử dụng nhiều bộ nhớ.

    • Xem dung lượng bộ nhớ mà mỗi đối tượng đang sử dụng.

  7. Hãy sử dụng thông tin này để thu hẹp hoặc tìm nguồn rò rỉ bộ nhớ và khắc phục.

Bước 5: Khắc phục lỗi rò rỉ bộ nhớ

Sau khi xác định được nguồn rò rỉ bộ nhớ, bạn có thể khắc phục. Việc khắc phục lỗi rò rỉ bộ nhớ trong ứng dụng Android giúp cải thiện hiệu suất và độ ổn định của ứng dụng. Tuỳ thuộc vào tình huống mà thông tin chi tiết sẽ khác nhau. Tuy nhiên, các đề xuất sau có thể giúp ích:

Các công cụ gỡ lỗi khác

Sau khi hoàn tất các bước này, nếu bạn vẫn không tìm thấy và khắc phục tình trạng rò rỉ bộ nhớ, hãy thử các công cụ sau:

Gỡ lỗi bộ nhớ trong mã gốc bằng theo dõi phân bổ

Ngay cả khi bạn không trực tiếp sử dụng mã gốc, một số thư viện Android phổ biến vẫn sử dụng được, bao gồm cả SDK của Google. Nếu bạn cho rằng sự cố rò rỉ bộ nhớ nằm trong mã gốc, thì bạn có thể sử dụng một số công cụ để gỡ lỗi. Tính năng theo dõi phân bổ bằng Android Studio hoặc heapprofd (cũng tương thích với Perfetto) là một cách tuyệt vời để xác định nguyên nhân tiềm ẩn gây rò rỉ bộ nhớ và thường là cách gỡ lỗi nhanh nhất.

Tính năng theo dõi phân bổ cũng có lợi thế nổi bật là cho phép bạn chia sẻ kết quả mà không bao gồm thông tin nhạy cảm có thể tìm thấy trong vùng nhớ khối xếp.

Xác định rò rỉ bằng LeakCanary

LeakCanary là một công cụ mạnh mẽ giúp xác định tình trạng rò rỉ bộ nhớ trong các ứng dụng Android. Để tìm hiểu thêm về cách sử dụng LeakCanary trong ứng dụng, hãy truy cập LeakCanary.

Cách báo cáo vấn đề về Google SDK

Nếu bạn đã thử các phương thức trong tài liệu này và nghi ngờ có sự cố rò rỉ bộ nhớ trong SDK của chúng tôi, hãy liên hệ với bộ phận hỗ trợ khách hàng và cung cấp nhiều thông tin sau đây nhất có thể:

  • Các bước tạo lại sự cố rò rỉ bộ nhớ. Nếu các bước này yêu cầu mã hoá phức tạp, bạn nên sao chép mã (trùng lặp vấn đề) vào ứng dụng mẫu và cung cấp thêm các bước cần thực hiện trong giao diện người dùng để kích hoạt sự cố rò rỉ.

  • Tệp báo lỗi được thu thập từ ứng dụng của bạn sau khi tạo lại vấn đề. Ghi lại các tệp kết xuất vùng nhớ khối xếp tại hai thời điểm khác nhau để cho thấy mức sử dụng bộ nhớ đã tăng đáng kể.

  • Nếu dự kiến sẽ có sự cố rò rỉ bộ nhớ gốc, hãy chia sẻ kết quả theo dõi phân bổ từ heapprofd.

  • Báo cáo lỗi được ghi nhận sau khi bạn tạo lại điều kiện rò rỉ.

  • Dấu vết ngăn xếp của mọi sự cố liên quan đến bộ nhớ.

    Lưu ý quan trọng: Chỉ riêng dấu vết ngăn xếp là chưa đủ để gỡ lỗi một vấn đề về bộ nhớ, vì vậy, hãy đảm bảo bạn cũng cung cấp một trong các dạng thông tin khác.