㈠ android基礎(29)內存泄漏
1)oom是什麼?
2)什麼情況導致oom?
3)有什麼解決方法可以避免OOM?
4)孫搏Oom 是否可以try catch?為什麼?
5)彎埋內存泄漏是什麼?
6)什麼情況導致內存泄漏?
7)如何防止線程的內存泄漏?
8)內存泄露場的解決方法
9)內存泄漏和內存溢出區別?
10)用IDE如何分析內存泄漏?
11)ANR產生的原因是什麼?
12)ANR定位和修正
13)廣播引起anr的時間限制是多少?
即 ML (Memory Leak)
指 程序在申請內存後,當該內存不需再使用 但 卻無法被釋放 & 歸還給 程序的現象。
從機制上的角度來說:
出現內存泄露的原因是 無意識地持有對象引用,使得 持有引用者的生命周期 > 被引用者的生命周期
a. 內存分配策略
由 ActivityManagerService 集中管理 所有進程的內存分配
b. 內存回收策略
a. 內存分配策略
用1個實例講解 內存分配
b. 內存則鬧祥釋放策略
靜態成員變數有個非常典型的例子 = 單例模式
解決方案
單例模式引用的對象的生命周期 = 應用的生命周期
多線程:AsyncTask、實現Runnable介面、繼承Thread類
Handler弱引用方式
㈡ 關於Android EditText導致的內存泄漏的問題
泄露信息如下:
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.
Displaying only 1 leak trace out of 4 with the same signature
Signature:
┬───
│ GC Root: System class
│
├─ android.os.AsyncTask class
│ Leaking: NO (a class is never leaking)
│ ↓ static AsyncTask.SERIAL_EXECUTOR
│ ~~~~~~~~~~~~~~~
├─ android.os.AsyncTask$SerialExecutor instance
│ Leaking: UNKNOWN
│ ↓ AsyncTask$SerialExecutor.mTasks
│ ~~~~~~
├─ java.util.ArrayDeque instance
│ Leaking: UNKNOWN
│ ↓ ArrayDeque.elements
│ ~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
├─ android.os.AsyncTask$SerialExecutor$1 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ AsyncTask$SerialExecutor$1.val$r
│ ~~~~~
├─ android.widget.TextView$3 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ TextView$3.this$0
│ ~~~~~~
├─ androidx.appcompat.widget.AppCompatEditText instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mID = R.id.input_edit
│ View.mWindowAttachCount = 1
│ mContext instance of RoomMainActivity with mDestroyed = true
│ ↓ View.mContext
╰→ RoomMainActivity instance
Leaking: YES (ObjectWatcher was watching this because RoomMainActivity
received Activity#onDestroy() callback and Activity#mDestroyed is true)
key = cb388a5c-0ff2-452f-a9b0-6ea7d03a5105
watchDurationMillis = 37631
retainedDurationMillis = 32630
mApplication instance of ChatApplication
mBase instance of androidx.appcompat.view.ContextThemeWrapper
====================================
原因分析:
1、引用鏈結構:AsyncTask的SerialExecutor執行器引用了EditText對象,而EditText對象中的mContext引用到了RoomMainActivity中context,導致RoomMainActivity 無法銷毀。
查看源碼得知在TextView中有updateTextServicesLocaleAsync()方法,調用了AsyncTask.execute()向其中傳入了匿名內部類Runnable,而持有了控制項對象。
2、解決方法:因為在源碼層面無法修改源碼,在引用端切斷引用鏈。
給EditText使用Application的上下文,在EditText使用的頁面退出銷毀時移除EditText控制項,包括置空它的監聽器、清除它的焦點。
import android.content.Context;
import android.os.Build;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.ViewGroup;
import androidx.appcompat.widget.AppCompatEditText;
/**
* 關於Android EditText導致的內存泄漏的問題
*/
public class NoMemoryLeakEditTextextends AppCompatEditText {
public NoMemoryLeakEditText(Context context) {
super(context.getApplicationContext());
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs) {
super(context.getApplicationContext(), attrs);
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context.getApplicationContext(), attrs, defStyleAttr);
}
public void clearMemoryLeak(TextWatcher watcher, ViewGroup container) {
clearFocus();
setOnTouchListener(null);
setOnClickListener(null);
setOnDragListener(null);
setOnKeyListener(null);
setOnLongClickListener(null);
setOnEditorActionListener(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
(null);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setOnScrollChangeListener(null);
}
setOnFocusChangeListener(null);
removeTextChangedListener(watcher);
container.removeView(this);
}
}
最後在頁面銷毀的地方調用clearMemoryLeak方法
㈢ Android中什麼情況下會導致內存泄露
集合類泄漏
集合類如果僅僅有添加元素的方法,而沒有相應的刪除機制,導致內存被佔用。如果這個集合類是全局性的變數 (比如類中的靜態屬性,全局性的 map 等即有靜態引用或 final 一直指向它),那麼沒有相應的刪除機制,很可能導致集合所佔用的內存只增不減。比如上面的典型例子就是其中一種情況,當然實際上我們在項目中肯定不會寫這么 2B 的代碼,但稍不注意還是很容易出現這種情況,比如我們都喜歡通過 HashMap 做一些緩存之類的事,這種情況就要多留一些心眼。
單例造成的內存泄漏
由於單例的靜態特性使得其生命周期跟應用的生命周期一樣長,所以如果使用不恰當的話,很容易造成內存泄漏
匿名內部類/非靜態內部類和非同步線程
非靜態內部類創建靜態實例造成的內存泄漏
有的時候我們可能會在啟動頻繁的Activity中,為了避免重復創建相同的數據資源
Handler 造成的內存泄漏
Handler 的使用造成的內存泄漏問題應該說是最為常見了,很多時候我們為了避免 ANR 而不在主線程進行耗時操作,在處理網路任務或者封裝一些請求回調等api都藉助Handler來處理,但 Handler 不是萬能的,對於 Handler 的使用代碼編寫一不規范即有可能造成內存泄漏。另外,我們知道 Handler、Message 和 MessageQueue 都是相互關聯在一起的,萬一 Handler 發送的 Message 尚未被處理,則該 Message 及發送它的 Handler 對象將被線程 MessageQueue 一直持有。
由於 Handler 屬於 TLS(Thread Local Storage) 變數, 生命周期和 Activity 是不一致的。因此這種實現方式一般很難保證跟 View 或者 Activity 的生命周期保持一致,故很容易導致無法正確釋放。
㈣ Android線程泄漏場景以及解決辦法
1.非靜態內部類的靜態實例
非靜態內部類會持有外部類的引用,如果非靜態內部類的實例是靜態的,就會長期的維持著外部類的引用,組織被系統回收,解決辦法是使用靜態內部類
2.多線程相關的匿名內部類和非靜態內部類
匿名內部類同樣會持有外部類的引用,如果在線程中執行耗時操作就有可能發生內存泄漏,導致外部類無法被回收,直到耗時任務結束,解決辦法是在頁面退出時結束線程中的任務
3.Handler內存泄漏
Handler導致的內存泄漏也可以被歸納為非靜態內部類導致的,Handler內部message是被存儲在MessageQueue中的,有些message不能馬上被處理,存在的時間會很長,導致handler無法被回收,如果handler是非靜態的,就會導致它的外部類無法被回收,解決辦法是1.使用靜態handler,外部類引用使用弱引用處理2.在退出頁面時移除消息隊列中的消息
4.Context導致內存泄漏
根據場景確定使用Activity的Context還是Application的Context,因為二者生命周期不同,對於不必須使用Activity的Context的場景(Dialog),一律採用Application的Context,單例模式是最常見的發生此泄漏的場景,比如傳入一個Activity的Context被靜態類引用,導致無法回收
5.靜態View導致泄漏
使用靜態View可以避免每次啟動Activity都去讀取並渲染View,但是靜態View會持有Activity的引用,導致無法回收,解決辦法是在Activity銷毀的時候將靜態View設置為null(View一旦被載入到界面中將會持有一個Context對象的引用,在這個例子中,這個context對象是我們的Activity,聲明一個靜態變數引用這個View,也就引用了activity)
6.WebView導致的內存泄漏
WebView只要使用一次,內存就不會被釋放,所以WebView都存在內存泄漏的問題,通常的解決辦法是為WebView單開一個進程,使用AIDL進行通信,根據業務需求在合適的時機釋放掉
7.資源對象未關閉導致
如Cursor,File,Broadcast等,內部往往都使用了緩沖,會造成內存泄漏,一定要確保關閉它並將引用置為null
8.集合中的對象未清理
集合用扮行團於保存對象,如果集合越來越大,不進行合理的清理,尤其是入股集合是靜態的
9.Bitmap導致內存泄漏
bitmap是比較占內存的,所以一定要在不使用的時候及時進行清理,避免靜態變數持有大的bitmap對象
10.監聽器未關閉
很多需要register和unregister的系統服務要在合適的時候進行unregister,手動添加的listener也廳橘需要帶顫及時移除
㈤ 使用ConnectivityManager的內存泄漏隱患
Android裡面內存泄漏問題最突出的就是Activity的泄漏,而泄漏的根源大多在於單例的使用,也就是一個靜態實例持有了Activity的引用。靜態變數的生命周期與應用(Application)是相同的,而鍵啟Activity生命周期通常比它短,也就會造成在Activity生命周期結束後,還被引用導致無法被系統回收釋放。
生成靜態引用內存泄漏可能有兩種情況:
這個主要講下系統級的情況,這樣的情況可能也有很多,舉個最近發現的問題ConnectivityManager。
通常我們獲取系統服務時採用如下方式:
在Android6.0系統上,如果這里的Context如果是Activity的實例,那麼即使你什麼也不幹也會造成內存泄漏。
用LeakCanary可以直接看到內存泄漏:
一步一步來分析下。
先從Context的getSystemService方法開始,我們知道Activity是從ContextWrapper繼承而來的,ContextWrapper中持有一個mBase實例,這個實例指向一個ContextImpl對象,同時ContextImpl對象持有一個OuterContext對象,對於Activity來說,這個OuterContext就是Activity對象。所以調用getSystemService最終會調用到ContextImpl的getSystemService方法。
在6.0上 ContextImpl 的getSystemService方法調用 SystemServiceRegistry 來完成。
SystemServiceRegistry提供ConnectivityManager的實例。
在6.0上, ConnectivityManager 實現為單例:
精彩的部分來了,ConnectivityManager 持慶亮賀有了一個Context的引用:
這個Context在ConnectivityManager 創建時傳入,這個Context在中由ContextImpl對象轉換為OuterContext,與就是Activity對象,所以最終ConnectivityManager的單實例持有了Activity的實例引用。這樣即使Activity退出後仍然無法釋放,導致譽派內存泄漏。
這個問題僅在6.0上出現,在5.1上ConnectivityManager實現為單例但不持有Context的引用,在5.0有以下版本ConnectivityManager既不為單例,也不持有Context的引用。
其他服務沒認真研究,不確定有沒有這個問題。不過為了避免類似的情況發生,最好的解決辦法就是:
㈥ Android技術分享|Android 中部分內存泄漏示例及解決方案
內存泄漏:
舉例:
請注意以下的例子是虛構的
內存抖動
源自Android文檔中的 Memory churn 一詞,中文翻譯為內存抖動。
指快速頻繁的創建對象從而產生的性能問題。
引用Android文檔原文:
Java內存泄漏的根本原因是 長生命周期 的對象持有 短生命周期 對象的引用就很可能發生內存泄漏。
盡管短生命周期對象已經不再需要,但因為長生命周期依舊持有它的引用,故不能被回收而導致內存泄漏。
靜態集合類引起的內存泄漏
如果僅僅釋放引用本身(tO = null), ArrayList 依然在引用該對象,GC無法回收。
監聽器
在Java應用中,通常會用到很多監聽器,一般通過 addXXXXListener() 實現。但釋放對象時通常會忘記刪除監聽器,從而增加內存泄漏的風險。
各種連接
如資料庫連接、網路連接(Socket)和I/O連接。忘記顯式調用 close() 方法引起的內存泄漏。
內部類和外部模塊的引用
內部類的引用是很容易被遺忘的一種,一旦沒有釋放可能會導致一系列後續對象無法釋放。此外還要小心外部模塊不經意的引用,內部類是否提供相應的操作去除外部引用。
單例模式
由於單例的靜態特性,使其生命周期與應用的生命周期一樣長,一旦使用不恰當極易造成內存泄漏。如果單利持有外部引用,需要注意提供釋放方式,否則當外部對象無法被正常回收時,會進而導致內存泄漏。
集合類泄漏
如集合的使用范圍超過邏輯代碼的范圍,需要格外注意刪除機制是否完善可靠。比如由靜態屬性 static 指向的集合。
單利泄漏
以下為簡單邏輯代碼,只為舉例說明內存泄漏問題,不保證單利模式的可靠性。
AppManager 創建時需要傳入一個 Context ,這個 Context 的生命周期長短至關重要。
1. 如果傳入的是 Application 的 Context ,因為 Application 的生命周期等同於應用的生命周期,所以沒有任何問題。
2. 如果傳入的是 Activity 的 Context ,則需要考慮這個 Activity 是否在整個生命周期都不會被回收了,如果不是,則會造成內存泄漏。
非靜態內部類創建靜態實例造成的內存泄漏
應該將該內部類單獨封裝為一個單例來使用。
匿名內部類/非同步線程
Runnable都使用了匿名內部類,將持有MyActivity的引用。如果任務在Activity銷毀前未完成,將導致Activity的內存無法被回收,從而造成內存泄漏。
解決方法:將Runnable獨立出來或使用靜態內部類,可以避免因持有外部對象導致的內存泄漏。
Handler造成的內存泄漏
Handler屬於TLS(Thread Local Storage)變數,生命周期與Activity是不一致的,容易導致持有的對象無法正確被釋放
當Android應用程序啟動時,該應用程序的主線程會自動創建一個Looper對象和與之關聯的MessageQueue。
當主線程中實例化一個Handler對象後,它就會自動與主線程Looper的MessageQueue關聯起來。所有發送到MessageQueue的Messag都會持有Handler的引用,所以Looper會據此回調Handle的handleMessage()方法來處理消息。只要MessageQueue中有未處理的Message,Looper就會不斷的從中取出並交給Handler處理。
另外,主線程的Looper對象會伴隨該應用程序的整個生命周期。
在Java中,非靜態內部類和匿名類內部類都會潛在持有它們所屬的外部類的引用,但是靜態內部類卻不會。
當該 Activity 被 finish() 掉時,延遲執行任務的 Message 還會繼續存在於主線程中,它持有該 Activity 的 Handler 引用,所以此時 finish() 掉的 Activity 就不會被回收了從而造成內存泄漏(因 Handler 為非靜態內部類,它會持有外部類的引用,在這里就是指 SampleActivity)。
避免不必要的靜態成員變數
對於BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等資源的使用,應在Activity銷毀前及時關閉或注銷。
不使用WebView對象時,應調用`destroy()`方法銷毀。
㈦ android內存泄漏怎麼解決
其實在Android中會造成內存泄露則首跡的情景無外乎兩種:
全局進程(process-global)的static變數。這個無視應用的狀態,持有Activity的強引用的怪物。
活在Activity生命周期之外的線程。沒有清空對Activity的強引用。
檢查一下你的項目中芹洞是否有以下幾種情況孫並:
Static Activities
Static Views
Inner Classes
Anonymous Classes
Handler
Threads
TimerTask
Sensor Manager
對應加以解決。
㈧ Android常見內存泄漏
如果 thread 為靜態變數,即使在 onDestroy 方法中,主動置空 thread 變數,依然會有內存泄漏。因為此時泄漏包含了 靜態變數 和 子線程為非靜態內部類實例 兩種情況,他們都持有 Activity ,且生命周期比 Activity 更長,線程相關的下盯毀液面三種泄露余余情況也是一樣的。
子線程在 SingleTest 單例中:
注意:
單例:
Activity:
單例:
Activity::
還有一種情況,把 registerReceiver(mReceiver, intentFilter) 改成 getApplication().getApplicationContext().registerReceiver(mReceiver, intentFilter) ,即 Application 代替 Activity 注冊廣播,如果 MyReceiver 和 MyFilter 也都是靜態類,不取消注冊不會導致 Activity 泄露,因為使用的是 Application 的上下文。但如果它哥倆有一個是內部類或者匿名類,也等於 AMS 間接持有 Activity ,則仍會引發 Activity 泄露。最好還是注冊廣播後,在銷毀時主動取消注冊。
及時 close 資源。
調用 bitmap.recycle() 回收。
及時清空集合中的對象。凱物