专项:Android 内存泄露实践分析

定义

​ 内存泄漏也称作 “存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。

内存泄漏形象的比喻是 “操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以 “内存泄漏” 是从操作系统的角度来看的。这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄漏了。

​ ——来自《百度百科》

影响

成效

内存泄露实施后,项目的收获:

类型

技巧

分析

原理

根本原因

怎么解决

实践分析

方案

实践(示例)

Bitmap 泄露

Bitmap 泄露一般会泄露较多内存,视图片大小、位图而定

App 启动图 Activity 的onDestroy()中及时回收内存

@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 流未关闭

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

静态变量持有 Context 实例泄露

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

Context 泄露

Callback 泄露

服务未解绑注册泄露

private void initSensor() {
        // 获取传感器管理器
        sm = (SensorManager) container.activity.getSystemService(Context.SENSOR_SERVICE);
        // 获取距离传感器
        acceleromererSensor = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        // 设置传感器监听器
        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;
  }
}

后面

未完待续。。。


↙↙↙阅读原文可查看相关链接,并与作者交流