摘要:如果這個靜態(tài)變量在生命周期結(jié)束后沒有清空,就導(dǎo)致內(nèi)存泄漏。因此造成內(nèi)存泄露。注冊沒取消造成的內(nèi)存泄露這種的內(nèi)存泄露比純的內(nèi)存泄漏還要嚴(yán)重,因?yàn)槠渌恍┏绦蚩赡芤孟到y(tǒng)的程序的對象比如注冊機(jī)制。
原文鏈接 更多教程 為什么會發(fā)生內(nèi)存泄漏
內(nèi)存空間使用完畢之后未回收, 會導(dǎo)致內(nèi)存泄漏。有人會問:Java不是有垃圾自動回收機(jī)制么?不幸的是,在Java中仍存在很多容易導(dǎo)致內(nèi)存泄漏的邏輯(logical leak)。雖然垃圾回收器會幫我們干掉大部分無用的內(nèi)存空間,但是對于還保持著引用,但邏輯上已經(jīng)不會再用到的對象,垃圾回收器不會回收它們。
例如
忘記釋放分配的內(nèi)存的。(Cursor忘記關(guān)閉等)。
應(yīng)用不再需要這個對象,未釋放該對象的所有引用。
強(qiáng)引用持有的對象,垃圾回收器是無法在內(nèi)存中回收這個對象。
持有對象生命周期過長,導(dǎo)致無法回收。
Java判斷無效對象的原理Android內(nèi)存回收管理策略圖:
圖中的每個圓節(jié)點(diǎn)代表對象的內(nèi)存資源,箭頭代表可達(dá)路徑。當(dāng)圓節(jié)點(diǎn)與 GC Roots 存在可達(dá)路徑時,表示當(dāng)前資源正被引用,虛擬機(jī)是無法對其進(jìn)行回收的(如圖中的黃色節(jié)點(diǎn))。反過來,如果圓節(jié)點(diǎn)與 GC Roots 不存在可達(dá)路徑,則意味著這塊對象的內(nèi)存資源不再被程序引用,系統(tǒng)虛擬機(jī)可以在 GC 過程中將其回收掉。
從定義上講,Android(Java)平臺的內(nèi)存泄漏是指沒有用的對象資源任與GC-Root保持可達(dá)路徑,導(dǎo)致系統(tǒng)無法進(jìn)行回收。
內(nèi)存泄漏帶來的危害用戶對單次的內(nèi)存泄漏并沒有什么感知,但當(dāng)泄漏積累到內(nèi)存都被消耗完,就會導(dǎo)致卡頓,崩潰。
內(nèi)存泄露是內(nèi)存溢出OOM的重要原因之一,會導(dǎo)致Crash
Android中常見的可能發(fā)生內(nèi)存泄漏的地方 1.在Android開發(fā)中,最容易引發(fā)的內(nèi)存泄漏問題的是Context。比如Activity的Context,就包含大量的內(nèi)存引用,一旦泄漏了Context,也意味泄漏它指向的所有對象。
造成Activity泄漏的常見原因:
Static Activities
在類中定義了靜態(tài)Activity變量,把當(dāng)前運(yùn)行的Activity實(shí)例賦值于這個靜態(tài)變量。
如果這個靜態(tài)變量在Activity生命周期結(jié)束后沒有清空,就導(dǎo)致內(nèi)存泄漏。
因?yàn)閟tatic變量是貫穿這個應(yīng)用的生命周期的,所以被泄漏的Activity就會一直存在于應(yīng)用的進(jìn)程中,不會被垃圾回收器回收。
static Activity activity; //這種代碼要避免
單例中保存Activity
在單例模式中,如果Activity經(jīng)常被用到,那么在內(nèi)存中保存一個Activity實(shí)例是很實(shí)用的。但是由于單例的生命周期是應(yīng)用程序的生命周期,這樣會強(qiáng)制延長Activity的生命周期,這是相當(dāng)危險而且不必要的,無論如何都不能在單例子中保存類似Activity的對象。
舉例:
public class Singleton { private static Singleton instance; private Context mContext; private Singleton(Context context){ this.mContext = context; } public static Singleton getInstance(Context context){ if (instance == null){ synchronized (Singleton.class){ if (instance == null){ instance = new Singleton(context); } } } return instance; } }
在調(diào)用Singleton的getInstance()方法時傳入了Activity。那么當(dāng)instance沒有釋放時,這個Activity會一直存在。因此造成內(nèi)存泄露。
解決方法:
可以將new Singleton(context)改為new Singleton(context.getApplicationContext())即可,這樣便和傳入的Activity沒關(guān)系了。
Static Views
同理,靜態(tài)的View也是不建議的
Inner Classes
內(nèi)部類的優(yōu)勢可以提高可讀性和封裝性,而且可以訪問外部類,不幸的是,導(dǎo)致內(nèi)存泄漏的原因,就是內(nèi)部類持有外部類實(shí)例的強(qiáng)引用。 例如在內(nèi)部類中持有Activity對象
解決方法:
1.將內(nèi)部類變成靜態(tài)內(nèi)部類;
2.如果有強(qiáng)引用Activity中的屬性,則將該屬性的引用方式改為弱引用;
3.在業(yè)務(wù)允許的情況下,當(dāng)Activity執(zhí)行onDestory時,結(jié)束這些耗時任務(wù);
例如:
發(fā)生內(nèi)存泄漏的代碼:
public class LeakAct extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); test(); } //這兒發(fā)生泄漏 public void test() { new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
解決方法:
public class LeakAct extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); test(); } //加上static,變成靜態(tài)匿名內(nèi)部類 public static void test() { new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
Anonymous Classes
匿名類也維護(hù)了外部類的引用。當(dāng)你在匿名類中執(zhí)行耗時任務(wù),如果用戶退出,會導(dǎo)致匿名類持有的Activity實(shí)例就不會被垃圾回收器回收,直到異步任務(wù)結(jié)束。
原文鏈接 更多教程Handler
handler中,Runnable內(nèi)部類會持有外部類的隱式引用,被傳遞到Handler的消息隊(duì)列MessageQueue中,在Message消息沒有被處理之前,Activity實(shí)例不會被銷毀了,于是導(dǎo)致內(nèi)存泄漏。
解決方法:
1.可以把Handler類放在多帶帶的類文件中,或者使用靜態(tài)內(nèi)部類便可以避免泄露;
2.如果想在Handler內(nèi)部去調(diào)用所在的Activity,那么可以在handler內(nèi)部使用弱引用的方式去指向所在Activity.使用Static + WeakReference的方式來達(dá)到斷開Handler與Activity之間存在引用關(guān)系的目的.
3.在界面銷毀是,釋放handler資源
@Override protected void doOnDestroy() { super.doOnDestroy(); if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } mHandler = null; mRenderCallback = null; }
同樣還有其他匿名類實(shí)例,如TimerTask、Threads等,執(zhí)行耗時任務(wù)持有Activity的引用,都可能導(dǎo)致內(nèi)存泄漏。
線程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控。如果我們的線程是Activity的內(nèi)部類,所以MyThread中保存了Activity的一個引用,當(dāng)MyThread的run函數(shù)沒有結(jié)束時,MyThread是不會被銷毀的,因此它所引用的老的Activity也不會被銷毀,因此就出現(xiàn)了內(nèi)存泄露的問題。
要解決Activity的長期持有造成的內(nèi)存泄漏,可以通過以下方法:傳入Application 的 Context,因?yàn)?Application 的生命周期就是整個應(yīng)用的生命周期,所以這將沒有任何問題。
如果此時傳入的是 Activity 的 Context,當(dāng)這個 Context 所對應(yīng)的 Activity 退出時,主動結(jié)束執(zhí)行的任務(wù),并釋放Activity資源。
將線程的內(nèi)部類,改為靜態(tài)內(nèi)部類。
因?yàn)榉庆o態(tài)內(nèi)部類會自動持有一個所屬類的實(shí)例,如果所屬類的實(shí)例已經(jīng)結(jié)束生命周期,但內(nèi)部類的方法仍在執(zhí)行,就會hold其主體(引用)。也就使主體不能被釋放,亦即內(nèi)存泄露。靜態(tài)類編譯后和非內(nèi)部類是一樣的,有自己獨(dú)立的類名。不會悄悄引用所屬類的實(shí)例,所以就不容易泄露。
如果需要引用Acitivity,使用弱引用。
謹(jǐn)慎對context使用static關(guān)鍵字。
2.Bitmap沒調(diào)用recycle()Bitmap對象在不使用時,我們應(yīng)該先調(diào)用recycle()釋放內(nèi)存,然后才設(shè)置為null.
3.集合中對象沒清理造成的內(nèi)存泄露我們通常把一些對象的引用加入到了集合中,當(dāng)我們不需要該對象時,并沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴(yán)重了。
解決方案:
在Activity退出之前,將集合里的東西clear,然后置為null,再退出程序。4.注冊沒取消造成的內(nèi)存泄露
這種Android的內(nèi)存泄露比純Java的內(nèi)存泄漏還要嚴(yán)重,因?yàn)槠渌恍〢ndroid程序可能引用系統(tǒng)的Android程序的對象(比如注冊機(jī)制)。即使Android程序已經(jīng)結(jié)束了,但是別的應(yīng)用程序仍然還有對Android程序的某個對象的引用,泄漏的內(nèi)存依然不能被垃圾回收。
解決方案:
1.使用ApplicationContext代替ActivityContext;5.資源對象沒關(guān)閉造成的內(nèi)存泄露
2.在Activity執(zhí)行onDestory時,調(diào)用反注冊;
資源性對象比如(Cursor,F(xiàn)ile文件等)往往都用了一些緩沖,我們在不使用的時候,應(yīng)該及時關(guān)閉它們,以便它們的緩沖及時回收內(nèi)存。而不是等待GC來處理。
6.占用內(nèi)存較多的對象(圖片過大)造成內(nèi)存溢出因?yàn)锽itmap占用的內(nèi)存實(shí)在是太多了,特別是分辨率大的圖片,如果要顯示多張那問題就更顯著了。Android分配給Bitmap的大小只有8M.
解決方法:
1.等比例縮小圖片
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2;//圖片寬高都為原來的二分之一,即圖片為原來的四分之一
2.對圖片采用軟引用,及時地進(jìn)行recycle()操作
//軟引用 SoftReference7.WebView內(nèi)存泄露(影響較大)bitmap = new SoftReference (pBitmap); //回收操作 if(bitmap != null) { if(bitmap.get() != null && !bitmap.get().isRecycled()){ bitmap.get().recycle(); bitmap = null; } }
解決方案:
用新的進(jìn)程起含有WebView的Activity,并且在該Activity 的onDestory() 最后加上 System.exit(0); 殺死當(dāng)前進(jìn)程。檢測內(nèi)存泄漏的方法
1.使用 靜態(tài)代碼分析工具-Lint 檢查內(nèi)存泄漏
Lint 是 Android Studio 自帶的工具,使用姿勢很簡單 Analyze -> Inspect Code 然后選擇想要掃面的區(qū)域即可
對可能引起泄漏的編碼,Lint 都會進(jìn)行溫馨提示:
2.LeakCanary 工具
Square 公司出品的內(nèi)存分析工具,官方地址如下:https://github.com/square/lea...
LeakCanary 需要在項(xiàng)目代碼中集成,不過代碼也非常簡單,如下的官方示例:
在你的 build.gradle:
dependencies { debugImplementation "com.squareup.leakcanary:leakcanary-android:1.6.3" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:1.6.3" // Optional, if you use support library fragments: debugImplementation "com.squareup.leakcanary:leakcanary-support-fragment:1.6.3" }
在 Application 類:
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); // Normal app init code... } }
當(dāng)內(nèi)存泄漏發(fā)生時,LeakCanary 會彈窗提示并生成對應(yīng)的堆存儲信息記錄
-3.Android Monitor
開Android Studio,編譯代碼,在模擬器或者真機(jī)上運(yùn)行App,然后點(diǎn)擊
,在Android Monitor下點(diǎn)擊Monitor對應(yīng)的Tab,進(jìn)入如下界面
在Memory一欄中,可以觀察不同時間App內(nèi)存的動態(tài)使用情況,點(diǎn)擊
可以手動觸發(fā)GC,點(diǎn)擊
可以進(jìn)入HPROF Viewer界面,查看Java的Heap,如下圖
Reference Tree代表指向該實(shí)例的引用,可以從這里面查看內(nèi)存泄漏的原因,Shallow Size指的是該對象本身占用內(nèi)存的大小,Retained Size代表該對象被釋放后,垃圾回收器能回收的內(nèi)存總和。
強(qiáng)引用(StrongReference):JVM 寧可拋出 OOM ,也不會讓 GC 回收具有強(qiáng)引用的對象;
軟引用(SoftReference):只有在內(nèi)存空間不足時,才會被回的對象;
弱引用(WeakReference):在 GC 時,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存;
虛引用(PhantomReference):任何時候都可以被GC回收,當(dāng)垃圾回收器準(zhǔn)備回收一個對象時,如果發(fā)現(xiàn)它還有虛引用,就會在回收對象的內(nèi)存之前,把這個虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。程序可以通過判斷引用隊(duì)列中是否存在該對象的虛引用,來了解這個對象是否將要被回收??梢杂脕碜鳛镚C回收Object的標(biāo)志。
原文鏈接 更多教程文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/77440.html
摘要:然而,中依然有可能發(fā)生內(nèi)存泄漏。所以你的安卓快速定位解決內(nèi)存泄漏掘金昨天是個好日子,程序員的節(jié)日,在這里給所有的程序員送上一份遲到的祝福。應(yīng)用內(nèi)存泄漏的定位分析與解決策略掘金,大家好,我是。 Android 性能優(yōu)化之巧用軟引用與弱引用優(yōu)化內(nèi)存使用 - Android - 掘金前言: 從事Android開發(fā)的同學(xué)都知道移動設(shè)備的內(nèi)存使用是非常敏感的話題,今天我們來看下如何使用軟引用與弱...
摘要:問題分析隨著回滾版本的放量,主端崩潰逐漸回歸正常,進(jìn)一步坐實(shí)了新版本存在問題。內(nèi)容非常多但都是重復(fù)的,看起來進(jìn)程沒有啟動,導(dǎo)致連接端一直在進(jìn)行重連。背景公司的主打產(chǎn)品是一款跨平臺的 App,我的部門負(fù)責(zé)為它提供底層的 sdk 用于數(shù)據(jù)傳輸,我負(fù)責(zé)的是 Adnroid 端的 sdk 開發(fā)。sdk 并不直接加載在 App 主進(jìn)程,而是隔離在一個多帶帶進(jìn)程中,然后兩個進(jìn)程通過 tcp 連接進(jìn)行通信...
閱讀 2725·2021-11-17 17:00
閱讀 2015·2021-10-11 10:57
閱讀 3846·2021-09-09 11:33
閱讀 1039·2021-09-09 09:33
閱讀 3616·2019-08-30 14:20
閱讀 3424·2019-08-29 11:25
閱讀 2874·2019-08-26 13:48
閱讀 818·2019-08-26 11:52