專項(xiàng):Android內(nèi)存泄露實(shí)踐分析定義內(nèi)存泄漏也稱作“存儲(chǔ)滲漏”,用動(dòng)態(tài)存儲(chǔ)分配函數(shù)動(dòng)態(tài)開辟的空間,在使用完畢后未釋放,結(jié)果導(dǎo)致一直占據(jù)該內(nèi)存單元。直到程序結(jié)束。(其實(shí)說白了就是該內(nèi)存空間使用完畢之后未回收)即所謂內(nèi)存泄漏。 內(nèi)存泄漏形象的比喻是“操作系統(tǒng)可提供給所有進(jìn)程的存儲(chǔ)空間正在被某個(gè)進(jìn)程榨干”,最終結(jié)果是程序運(yùn)行時(shí)間越長(zhǎng),占用存儲(chǔ)空間越來越多,最終用盡全部存儲(chǔ)空間,整個(gè)系統(tǒng)崩潰。所以“內(nèi)存泄漏”是從操作系統(tǒng)的角度來看的。這里的存儲(chǔ)空間并不是指物理內(nèi)存,而是指虛擬內(nèi)存大小,這個(gè)虛擬內(nèi)存大小取決于磁盤交換區(qū)設(shè)定的大小。由程序申請(qǐng)的一塊內(nèi)存,如果沒有任何一個(gè)指針指向它,那么這塊內(nèi)存就泄漏了。 ——來自《百度百科》
影響導(dǎo)致OOM 糟糕的用戶體驗(yàn) 雞肋的App存活率
成效內(nèi)存泄露是一個(gè)持續(xù)的過程,隨著版本的迭代,效果越明顯 由于某些原因無法改善的泄露(如框架限制),則盡量降低泄露的內(nèi)存大小 內(nèi)存泄露實(shí)施后的版本,一定要驗(yàn)證,不必馬上推行到正式版,可作為beta版持續(xù)觀察是否影響/引發(fā)其他功能/問題
內(nèi)存泄露實(shí)施后,項(xiàng)目的收獲: 類型IO Bitmap Context Service BraodcastReceiver ContentObserver
Handler Thread
技巧
善用Reference 類型 | 垃圾回收時(shí)間 | 生存時(shí)間 |
---|
強(qiáng)引用 | 永遠(yuǎn)不會(huì) | JVM停止運(yùn)行時(shí)終止 | 軟引用 | 內(nèi)存不足時(shí) | 內(nèi)存不足時(shí)終止 | 弱引用 | 垃圾回收時(shí) | 垃圾回收時(shí)終止 | 虛引用 | 垃圾回收時(shí) | 垃圾回收時(shí)終止 |
復(fù)用ConvertView 對(duì)象釋放
分析 原理 根本原因 怎么解決 實(shí)踐分析 方案StrictMode StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy .Builder() .detectAll() .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy .Builder() .detectAll() .penaltyLog() .build());
Leakcanary Leakcanary + StrictMode + monkey (推薦) 使用階段:功能測(cè)試完成后,穩(wěn)定性測(cè)試開始時(shí) 使用方法:安裝集成了Leakcanary的包,跑monkey 收獲階段:一段時(shí)間后,會(huì)發(fā)現(xiàn)出現(xiàn)N個(gè)泄露 實(shí)戰(zhàn)分析:逐條分析每個(gè)泄露并改善/修復(fù) StrictMode:查看日志搜索StrictMode關(guān)鍵字
Adb命令 Android Monitor MAT
實(shí)踐(示例)Bitmap泄露 Bitmap泄露一般會(huì)泄露較多內(nèi)存,視圖片大小、位圖而定 App啟動(dòng)圖Activity的onDestroy() 中及時(shí)回收內(nèi)存 @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); recycleImageView(imgv_load_ad); }
public static void recycleImageView(View view){ if(view==null) return; if(view instanceof ImageView){ Drawable drawable=((ImageView) view).getDrawable(); if(drawable instanceof BitmapDrawable){ Bitmap bmp = ((BitmapDrawable)drawable).getBitmap(); if (bmp != null && !bmp.isRecycled()){ ((ImageView) view).setImageBitmap(null); bmp.recycle(); bmp=null; } } } }
IO流未關(guān)閉 public static void copyFile(File source, File dest) { FileChannel inChannel = null; FileChannel outChannel = null; Log.i(TAG, "source path: " + source.getAbsolutePath()); Log.i(TAG, "dest path: " + dest.getAbsolutePath()); try { inChannel = new FileInputStream(source).getChannel(); outChannel = new FileOutputStream(dest).getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); } catch (IOException e) { e.printStackTrace(); } }
public static void copyFile(File source, File dest) { FileChannel inChannel = null; FileChannel outChannel = null; Log.i(TAG, "source path: " + source.getAbsolutePath()); Log.i(TAG, "dest path: " + dest.getAbsolutePath()); try { inChannel = new FileInputStream(source).getChannel(); outChannel = new FileOutputStream(dest).getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); } catch (IOException e) { e.printStackTrace(); } finally { if (inChannel != null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel != null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } }
E/StrictMode: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks. java.lang.Throwable: Explicit termination method 'close' not called at dalvik.system.CloseGuard.open(CloseGuard.java:180) at java.io.FileOutputStream.<init>(FileOutputStream.java:89) at java.io.FileOutputStream.<init>(FileOutputStream.java:72) at com.heyniu.lock.utils.FileUtil.copyFile(FileUtil.java:44) at com.heyniu.lock.db.BackupData.backupData(BackupData.java:89) at com.heyniu.lock.ui.HomeActivity$11.onClick(HomeActivity.java:675) at android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:157) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
單例模式泄露 ActivityUtil.getAppManager().add(this);
public void add(Activity activity) { if (activityStack == null) { synchronized (ActivityUtil.class){ if (activityStack == null) { activityStack = new Stack<>(); } } } activityStack.add(activity); }
@Override protected void onDestroy() { super.onDestroy(); ActivityUtil.getAppManager().remove(this); }
靜態(tài)變量持有Context實(shí)例泄露 private static HttpRequest req; public static void HttpUtilPost(Context context, int TaskId, String url, String requestBody,ArrayList<HttpHeader> Headers, RequestListener listener) { // TODO Auto-generated constructor stub req = new HttpRequest(context, url, TaskId, requestBody, Headers, listener); req.post(); }
解決方案: public static void cancel(int TaskId) { if(req != null && req.get() != null){ req.get().AsyncCancel(TaskId); } }
private static WeakReference<HttpRequest> req; public static void HttpUtilPost(Context context, int TaskId, String url, String requestBody,ArrayList<HttpHeader> Headers, RequestListener listener) { // TODO Auto-generated constructor stub req = new WeakReference<HttpRequest>(new HttpRequest(context, url, TaskId, requestBody, Headers, listener)); req.get().post(); }
private static HttpRequest req; public static void HttpUtilPost(Context context, int TaskId, String url, String requestBody,ArrayList<HttpHeader> Headers, RequestListener listener) { // TODO Auto-generated constructor stub req = new HttpRequest(context.getApplicationContext(), url, TaskId, requestBody, Headers, listener); req.post(); }
Context泄露 Callback泄露 服務(wù)未解綁注冊(cè)泄露 private void initSensor() { // 獲取傳感器管理器 sm = (SensorManager) container.activity.getSystemService(Context.SENSOR_SERVICE); // 獲取距離傳感器 acceleromererSensor = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); // 設(shè)置傳感器監(jiān)聽器 acceleromererListener = new SensorEventListener() { ...... }; sm.registerListener(acceleromererListener, acceleromererSensor, SensorManager.SENSOR_DELAY_NORMAL); }
@Override protected void onDestroy() { super.onDestroy(); sm.unregisterListener(acceleromererListener,acceleromererSensor); }
Handler泄露 handler.sendEmptyMessage(0);
@Override protected void onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null); }
異步線程泄露 new Thread() { public void run() { imageArray = loadImageFromUrl(imageUrl); }.start();
thread = new Thread() { public void run() { imageArray = loadImageFromUrl(imageUrl); }; thread.start();
@Override protected void onDestroy() { super.onDestroy(); if(thread != null){ thread.interrupt(); thread = null; } }
后面
|