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ề các phương pháp hay nhất cho ứng dụng Android trong quá trình 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ớ đã phân bổ mà không còn cần thiết nữa. Rò rỉ có thể khiến ứng dụng yêu cầu hệ điều hành cấp thêm bộ nhớ so với bộ nhớ hiện có, do đó làm ứng dụng gặp sự cố. Một số phương pháp không phù hợp có thể gây ra tình trạng rò rỉ bộ nhớ trong các ứng dụng Android, chẳng hạn như không xử lý tài nguyên đúng cách hoặc không huỷ đăng ký trình nghe khi không còn cần thiết.

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 chặn, phát hiện và giải quyết tình trạng rò rỉ bộ nhớ trong mã của bạn. Nếu bạn đã thử các phương pháp trong tài liệu này và nghi ngờ có rò rỉ bộ nhớ trong các SDK của chúng tôi, hãy xem Cách báo cáo vấn đề với các SDK của Google.

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

Trước khi báo cáo lỗi rò rỉ bộ nhớ cho nhóm hỗ trợ của 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 được cung cấp trong tài liệu này để đảm bảo lỗi không nằm trong mã của bạn. Các bước này có thể giải quyết vấn đề của bạn. Nếu không, các bước này sẽ tạo ra thông tin mà nhóm hỗ trợ của Google cần để giúp bạn.

Ngăn tình trạng rò rỉ bộ nhớ

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

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

Kiểm tra để đảm bảo bạn đã thực hiện tất cả những việc sau trong ứng dụng Android:

  1. Giải phóng tài nguyên không dùng đến.
  2. Huỷ đăng ký trình nghe khi không cần thiết nữa.
  3. Huỷ các tác vụ 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 mới nhất của SDK

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

Giải phóng 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 dùng nữa. Nếu không, tài nguyên sẽ tiếp tục chiếm bộ nhớ ngay cả sau khi ứng dụng của bạn hoàn tất việc sử dụng 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 tham chiếu GoogleMap cũ trong GeoSDK

Một lỗi thường gặp là GoogleMap có thể gây ra tình trạng 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ừ đó GoogleMap đượ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 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 các 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 thiết nữa

Khi ứng dụng Android của bạn đăng ký một 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 trạng thái của một khung hiển thị, hãy nhớ huỷ đăng ký trình nghe khi ứng dụng không còn cần theo dõi sự kiện đó nữa. Nếu không, các trình nghe sẽ tiếp tục chiếm bộ nhớ ngay cả sau khi ứng dụng của bạn hoàn tất.

Ví dụ: giả sử ứng dụng của bạn dùng Navigation SDK và gọi trình nghe sau đây để 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ỷ các tác vụ 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 tác vụ đó hoàn tất. Nếu không bị huỷ, tác vụ sẽ tiếp tục chạy ở chế độ nền ngay cả sau khi ứng dụng hoàn tất tác vụ đó.

Để biết thêm thông tin chi tiết về các phương pháp hay nhất, hãy xem bài viết 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 để phát hành tài nguyên

Nếu ứng dụng của bạn sử dụng Navigation SDK hoặc Maps SDK, hãy nhớ giải phóng các tài nguyên bằng cách chuyển tiếp các phương thức vòng đời (hiển thị bằng chữ 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 Navigation SDK hoặc MapView trong Maps SDK hoặc Navigation SDK. Bạn cũng có thể dùng SupportNavigationFragment hoặc SupportMapFragment thay vì trực tiếp dùng 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 mới nhất của các SDK

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

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

Nếu bạn vẫn thấy rò rỉ bộ nhớ sau khi triển khai tất cả các đề xuất áp dụng được trước đó trong tài liệu này, hãy làm theo quy trình này để 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ề hoạt động quản lý bộ nhớ trên Android.

Để gỡ lỗi rò rỉ bộ nhớ, hãy làm theo quy trình này:

  1. Tái hiện vấn đề. Bước này là bước cần thiết để gỡ lỗi.
  2. Kiểm tra xem mức sử dụng bộ nhớ có đúng như dự kiến hay không. Kiểm tra để đảm bảo rằng mức sử dụng tăng lên (có vẻ như là rò rỉ) không phải là bộ nhớ cần thiết để chạy ứng dụng của bạn.
  3. Gỡ lỗi ở cấp cao. Bạn có thể sử dụng một số tiện ích để gỡ lỗi. Có 3 bộ công cụ tiêu chuẩn giúp gỡ lỗi các vấn đề về bộ nhớ trong Android: Android Studio, Perfetto và các 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. Nhận một tệp báo lỗi và theo dõi việc phân bổ, sau đó phân tích tệp đó.
  5. Khắc phục lỗi rò rỉ bộ nhớ.

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

Bước 1: Mô phỏng lại vấn đề

Nếu bạn không thể tái tạo vấn đề, trước tiên hãy xem xét các trường hợp có thể dẫn đến tình trạng rò rỉ bộ nhớ. Bạn có thể chuyển thẳng đến việc xem một kết xuất heap nếu biết vấn đề đã được tái tạo. Tuy nhiên, nếu chỉ nhận được một kết xuất heap 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 chưa kích hoạt các điều kiện để kích hoạt một rò rỉ. Hãy cân nhắc việc thử nhiều tình huống khi cố gắng tái hiện vấn đề:

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

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

    • Bạn đã thử kích hoạt chuỗi này nhiều lần chưa?
  • Ứng dụng đã trải qua những trạng thái nào trong vòng đời?

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

Đảm bảo rằng bạn có thể tái tạo vấn đề trong phiên bản mới nhất của SDK. Vấn đề trong 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ớ của ứng dụng có đúng như dự kiến hay không

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

  • Có thể là một rò rỉ: Việc kích hoạt tình huống thông qua nhiều lần lặp lại sẽ làm tăng mức sử dụng bộ nhớ 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 dừng tình huống.

  • Mức sử dụng bộ nhớ có thể dự kiến: Mức sử dụng bộ nhớ tăng lên 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 có giới hạn 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ó khả năng là mức sử dụng bộ nhớ dự kiến, thì bạn có thể giải quyết vấn đề bằng cách quản lý bộ nhớ của ứng dụng. Để được trợ giúp, hãy xem bài viết 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 đó đi sâu vào chi tiết sau khi bạn thu hẹp các khả năng. Trước tiên, hãy dùng một trong những công cụ gỡ lỗi cấp cao này để phân tích 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 về bộ nhớ đã dùng. Bạn cũng có thể kích hoạt tính năng theo dõi việc phân bổ và tệp báo lỗi từ cùng một 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 phần 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 gì bạn có thể theo dõi bằng Perfetto cũng có sẵn dưới dạng một 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ối đa (maxRSS) mà ứng dụng yêu cầu theo thời gian. MaxPSS có thể không chính xác bằng. Để tăng độ chính xác, hãy xem cờ adb shell dumpsys procstats --help –start-testing.

Theo dõi việc phân bổ

Tính năng theo dõi việc phân bổ xác định dấu vết ngăn xếp nơi bộ nhớ được phân bổ và liệu bộ nhớ đó có được giải phóng hay không. Bước này đặc biệt hữu ích khi theo dõi các 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 đâ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 ra cách tái tạo vấn đề. Để biết các bước sử dụng tính năng theo dõi mức 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 mức phân bổ.

Bước 4: Kiểm tra mức sử dụng bộ nhớ của ứng dụng bằng một kết xuất heap

Một cách để phát hiện lỗi rò rỉ bộ nhớ là lấy báo lỗi bộ nhớ của ứng dụng rồi kiểm tra xem có lỗi rò rỉ hay không. Tệp báo lỗi là thông tin tổng quan nhanh về tất cả các đối tượng trong bộ nhớ của một ứng dụng. Bạn có thể dùng công cụ này để chẩn đoán tình trạng 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 lỗi rò rỉ bộ nhớ mà GC không thể khắc phục. Khi bạn ghi lại một vùng nhớ khối xếp, Android Studio sẽ kiểm tra xem có hoạt động hoặc mảnh nào vẫn có thể truy cập nhưng đã bị huỷ hay không.

  1. Ghi lại một 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 tình trạng 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 lại 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ớ của Android Studio.

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

Để chụp một kết xuất vùng nhớ khối xếp 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 rồi chuyển đến thư mục chứa các công cụ adb.
  3. Để ghi lại một 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 kết xuất vùng nhớ khối xếp, hãy chạy lệnh sau:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

Sử dụng Android Studio để ghi lại 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 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 chụp một 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 tệp đó. Để thực hiện việc này, hãy làm theo các bước sau:

  1. Mở dự án Android của bạn trong Android Studio.

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

  3. Mở thẻ Android Profiler.

  4. Chọn Kỷ niệm.

  5. Chọn Open heap dump (Mở kết xuất heap) rồi chọn tệp kết xuất heap 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 kết xuất heap:

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

    • Xác định những đố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 phạm vi hoặc tìm ra nguồn gốc của tình trạng rò rỉ bộ nhớ và khắc phục vấn đề.

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

Sau khi xác định được nguồn gốc của tình trạng rò rỉ bộ nhớ, bạn có thể khắc phục vấn đề này. Việc khắc phục tình trạng 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, những đề 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 chưa tìm thấy và khắc phục được lỗi rò rỉ bộ nhớ, hãy thử các công cụ sau:

Gỡ lỗi bộ nhớ trong mã gốc bằng tính năng theo dõi việc 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, bao gồm cả các SDK của Google. Nếu bạn cho rằng lỗi 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. Việc theo dõi hoạt động phân bổ bằng Android Studio hoặc heapprofd (cũng tương thích với Perfetto) là một cách hiệu quả để xác định các nguyên nhân tiềm ẩn gây ra tình trạng rò rỉ bộ nhớ và thường là cách gỡ lỗi nhanh nhất.

Tính năng theo dõi việc phân bổ cũng có ưu điểm riêng biệt là cho phép bạn chia sẻ kết quả mà không cần đưa thông tin nhạy cảm có trong một vùng nhớ đệm.

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

LeakCanary là một công cụ mạnh mẽ để 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 vào LeakCanary.

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

Nếu bạn đã thử các phương pháp trong tài liệu này và nghi ngờ 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 càng nhiều thông tin sau đây càng tốt:

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

  • Tệp báo lỗi được ghi lại từ ứng dụng của bạn khi vấn đề được tái tạo. Ghi lại tệp báo lỗi tại 2 thời điểm khác nhau cho thấy mức sử dụng bộ nhớ đã tăng lên đáng kể.

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

  • Báo cáo lỗi được ghi lại sau khi bạn tái tạo đ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: Bản thân dấu vết ngăn xếp thường không đủ để gỡ lỗi vấn đề về bộ nhớ, vì vậy, hãy nhớ cung cấp thêm một trong các dạng thông tin khác.