記憶體管理最佳做法

本文件假設您已採取 Android 應用程式的記憶體管理最佳做法,例如「管理應用程式的記憶體」一文所述的方式。

簡介

「記憶體流失」是一種資源流失,如果系統分配給電腦程式的記憶體不再需要使用卻沒有釋出,就會發生這種情況,可能導致應用程式要求的記憶體超過作業系統負荷,進而造成應用程式停止運作。有幾種不當做法可能會導致 Android 應用程式發生記憶體流失的問題,例如不當處理資源,或是未取消註冊不再需要使用的事件監聽器。

本文件將提供幾項最佳做法,協助您防止、偵測及解決程式碼發生記憶體流失的問題。如果試過本文件所述的方法,但還是認為我們的 SDK 可能有記憶體流失的問題,請參閱「如何回報 Google SDK 相關問題」一節。

與支援團隊聯絡之前

向 Google 支援團隊回報記憶體流失的問題前,請遵循本文件提供的最佳做法和偵錯步驟,確保程式碼不會發生這項錯誤。這些步驟可能可以解決問題,但如果無效,也能產生 Google 支援團隊為您提供協助時所需的資訊。

防止記憶體流失

對於使用 Google SDK 的程式碼,遵循這些最佳做法,有助於避免一些容易導致記憶體流失的因素。

Android 應用程式適用的最佳做法

請確認您已在 Android 應用程式中完成下列所有步驟:

  1. 釋出未使用的資源
  2. 取消註冊不再需要使用的事件監聽器
  3. 取消不再需要執行的任務
  4. 轉送生命週期方法以釋出資源
  5. 使用最新版 SDK

如要瞭解每項做法的詳細資訊,請參閱下列章節。

釋出未使用的資源

如果您的 Android 應用程式會使用資源,請務必在不再需要使用時釋出資源,否則在應用程式使用後,資源仍會占用記憶體。詳情請參閱 Android 說明文件中的「活動生命週期」一文。

釋出 GeoSDKs 中過時的 GoogleMap 參照

常見錯誤是使用 NavigationView 或 MapView 快取,這樣 GoogleMap 可能會導致記憶體流失。GoogleMap 與擷取來源的 NavigationView 或 MapView 是一對一關係。請確保未快取 GoogleMap,或在呼叫 NavigationView#onDestroy 或 MapView#onDestroy 時釋出參照。如果使用 NavigationSupportFragment、MapSupportFragment,或您自己用來納入這些檢視畫面的片段,就必須在 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
  }
}

取消註冊不再需要使用的事件監聽器

如果您的 Android 應用程式會註冊事件 (例如按鈕點擊或檢視畫面狀態變化) 的監聽器,請務必在應用程式不再需要監控該事件時,取消註冊監聽器,否則在應用程式使用完後,事件監聽器仍會占用記憶體。

舉例來說,假設您的應用程式使用 Navigation SDK,且會呼叫事件監聽器 addArrivalListener (這個方法可用於監聽抵達事件) 來監聽抵達事件。那麼,當應用程式不再需要監控抵達事件時,也應呼叫 removeArrivalListener

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()
}

取消不再需要執行的任務

當 Android 應用程式開始執行非同步任務時 (例如下載或網路要求),請務必在完成後取消任務,否則任務會繼續在背景執行。

如要進一步瞭解這些最佳做法,請參閱 Android 說明文件中的「管理應用程式的記憶體」一文。

轉送生命週期方法以釋出資源

如果您的應用程式使用 Navigation 或 Maps SDK,請務必將生命週期方法 (粗體顯示) 轉送至 navView 以釋出資源。如要這樣做,您可以在 Navigation SDK 中使用 NavigationView,或是在 Maps 或 Navigation SDK 中使用 MapView。此外,您也可以使用 SupportNavigationFragmentSupportMapFragment,而不是直接分別使用 NavigationViewMapView。Support Fragment 會處理生命週期方法的轉送作業。

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()
  */
}

使用最新版 SDK

Google SDK 會持續更新,加入新功能、修正錯誤並提升效能。請將應用程式中的 SDK 保持在最新狀態,以便接收這些修正項目。

記憶體流失問題偵錯

如果在採行本文前述的所有適用建議後,仍發生記憶體流失的問題,請按照下列程序進行偵錯。

開始之前,建議您先熟悉 Android 管理記憶體的方式。如需詳情,請參閱 Android 的「記憶體管理總覽」一文。

如要為記憶體流失問題進行偵錯,請按照下列程序操作:

  1. 重現問題。這是偵錯時不可或缺的步驟。
  2. 檢查記憶體用量是否正常。請檢查看似因流失而增加的用量,是否為執行應用程式所需的記憶體。
  3. 大範圍進行偵錯。您可以使用幾個公用程式來進行偵錯。以下三個標準工具組有助於為 Android 進行記憶體問題偵錯:Android Studio、Perfetto 和 Android Debug Bridge (ADB) 指令列公用程式。
  4. 查看應用程式的記憶體使用情況。您可以取得記憶體快照資料並追蹤配置,然後再分析結果。
  5. 修正記憶體流失問題

下列章節將詳細說明上述步驟。

步驟 1:重現問題

如果您無法重現問題,請先思考可能會導致記憶體流失的情況。如果您確定問題已經重現,直接查看記憶體快照資料可能管用。不過,如果您只是在應用程式啟動或其他任意時間點取得記憶體快照資料,則可能尚未達成觸發流失問題的條件。重現問題時,建議您研究各種可能的情況:

  • 啟用了哪組功能?

  • 哪些特定的使用者操作順序觸發了流失問題?

    • 您是否反覆多次嘗試執行這個順序?
  • 應用程式經歷了哪些生命週期狀態?

    • 您是否反覆多次嘗試經歷各種生命週期狀態?

請確認您可以在最新版本的 SDK 中重現問題。先前版本的問題可能已經修正。

步驟 2:檢查應用程式的記憶體用量是否正常

每項功能都需要使用額外的記憶體。為各種情況進行偵錯時,請思考這是正常使用情況,還是確實屬於記憶體流失。舉例來說,對於不同的功能或使用者任務,您可以思考下列可能性:

  • 很有可能屬於流失:反覆多次重現當時情況後,記憶體用量長時間下來有所增加。

  • 很有可能為正常記憶體使用行為:情況停止後,系統收回記憶體。

  • 可能為正常記憶體使用行為:記憶體用量在一段時間內增加,之後逐漸下降,這可能是因為快取有限或其他正常記憶體使用行為所致。

如果應用程式行為很有可能是正常的記憶體使用情況,那就可以透過管理應用程式的記憶體來解決問題。如需相關協助,請參閱「管理應用程式的記憶體」一文。

步驟 3:大範圍進行偵錯

為記憶體流失問題進行偵錯時,請先從大範圍著手,等到找出可能原因後再細查。您可以使用下列其中一項大範圍偵錯工具,先分析一段時間內是否出現流失情況:

Android Studio 記憶體分析器

這項工具提供記憶體用量的直方圖。您也可以透過這個介面取得記憶體快照資料並追蹤配置。這是我們預設建議使用的工具。詳情請參閱 Android Studio 記憶體分析器

Perfetto 記憶體計數器

Perfetto 可讓您精準掌控和追蹤數個指標,並集中顯示在同一張直方圖上。詳情請參閱 Perfetto 記憶體計數器

Perfetto 使用者介面

Android Debug Bridge (ADB) 指令列公用程式

Perfetto 可追蹤的許多指標也能在 adb 指令列公用程式中找到,方便您直接查詢。以下是幾個重要例子:

  • Meminfo 可讓您查看特定時間點的詳細記憶體資訊。

  • Procstats 可提供一段時間內的部分重要匯總統計資料。

這裡最值得注意的統計資料,就是應用程式一段時間內需要的最高實體記憶體用量 (maxRSS)。MaxPSS 不一定準確,如要提高準確度,請參閱 adb shell dumpsys procstats --help –start-testing 標記。

配置追蹤

配置追蹤可識別系統分配記憶體的堆疊追蹤,以及記憶體是否未釋出。如要追蹤原生程式碼的記憶體流失問題,這個步驟就格外實用。這項工具可識別堆疊追蹤,因此很適合用來快速解決根本原因,或是找出重現問題的方式。如要瞭解如何使用配置追蹤,請參閱「透過配置追蹤為原生程式碼中的記憶體進行偵錯」一節。

步驟 4:查看記憶體快照資料,瞭解應用程式的記憶體使用情況

想要偵測記憶體流失,其中一種方法是取得應用程式的記憶體快照資料,然後檢查是否有流失問題。記憶體快照資料是應用程式記憶體中所有物件的快照,可用來診斷記憶體流失等記憶體相關問題。

Android Studio 可偵測垃圾收集器無法修正的記憶體流失問題。擷取記憶體快照資料時,Android Studio 會確認是否有仍可存取但已刪除的活動或片段。

  1. 擷取記憶體快照資料
  2. 分析記憶體快照資料來找出記憶體流失問題
  3. 修正記憶體流失問題

詳情請參閱以下各節說明。

擷取記憶體快照資料

如要擷取記憶體快照資料,您可以使用 Android Debug Bridge (ADB) 或 Android Studio 記憶體分析器。

使用 ADB 擷取記憶體快照資料

如要使用 ADB 擷取記憶體快照資料,請按照下列步驟操作:

  1. 將 Android 裝置連接到電腦。
  2. 開啟命令提示字元視窗,然後前往 ADB 工具所在的目錄。
  3. 如要擷取記憶體快照資料,請執行以下指令:

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. 如要取回記憶體快照資料,請執行以下指令:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

使用 Android Studio 擷取記憶體快照資料

如要使用 Android Studio 記憶體分析器擷取記憶體快照資料,請按照 Android 中「擷取記憶體快照資料」一節的步驟操作。

分析記憶體快照資料來找出記憶體流失問題

擷取記憶體快照資料後,您可以使用 Android Studio 記憶體分析器加以分析,步驟如下:

  1. 在 Android Studio 中開啟 Android 專案。

  2. 依序選取「Run」(執行) 和「Debug」(偵錯) 設定

  3. 開啟「Android Profiler」(Android 分析器) 分頁。

  4. 選取「Memory」(記憶體)

  5. 選取「Open heap dump」(開啟記憶體快照資料),然後選取您要產生的記憶體快照資料檔案。記憶體分析器會顯示應用程式記憶體用量的圖表。

  6. 運用圖表分析記憶體快照資料:

    • 找出不再使用的物件。

    • 找出使用大量記憶體的物件。

    • 瞭解每個物件目前的記憶體用量。

  7. 根據這項資訊細查或找出記憶體流失的原因,然後加以修正。

步驟 5:修正記憶體流失問題

找出記憶體流失的原因後,即可進行修正。修正 Android 應用程式的記憶體流失問題,有助於提升應用程式的效能和穩定度,細節則視情況而定。不過,下列建議可能對您有幫助:

其他偵錯工具

完成上述步驟後,如果還是找不到也無法修正記憶體流失問題,請使用下列工具:

透過配置追蹤為原生程式碼中的記憶體進行偵錯

即使您不直接使用原生程式碼,一些常見的 Android 程式庫 (包括 Google SDK) 也會使用。如果認為原生程式碼有記憶體流失問題,可以使用幾項工具進行偵錯。想要找出記憶體流失的可能原因,使用 Android Studioheapprofd (也與 Perfetto 相容) 追蹤配置是不錯的做法,通常也是最快的偵錯方式。

配置追蹤還有一項獨特優勢,就是可以讓您分享結果,不必附上能在堆積中找到的機密資訊。

使用 LeakCanary 找出流失問題

LeakCanary 可有效找出 Android 應用程式的記憶體流失問題。如要進一步瞭解如何在應用程式中使用 LeakCanary,請前往 LeakCanary

如何回報 Google SDK 相關問題

如果試過本文件所述的方法,但還是認為我們的 SDK 可能有記憶體流失的問題,請與客戶支援團隊,並盡可能提供下列資訊,越多越好:

  • 重現記憶體流失問題的步驟。如果這些步驟需要複雜編碼才能完成,建議您將能重現問題的程式碼複製到我們的範例應用程式中,並提供在使用者介面中觸發流失問題所需採取的額外步驟。

  • 從重現問題的應用程式中擷取的記憶體快照資料。請擷取兩個不同時間點的記憶體快照資料,呈現記憶體用量大幅增加的情況。

  • 如果原生記憶體流失屬於正常情況,請提供來自 heapprofd 的配置追蹤結果。

  • 重現流失情況後產出的錯誤報告

  • 所有記憶體相關當機的堆疊追蹤

    重要注意事項:堆疊追蹤本身通常並不足以為記憶體問題進行偵錯,因此請務必一併提供任何一種其他形式的資訊。