Android areaAndroid 技术开发首页投稿(暂停使用,暂停投稿)

让内存泄漏检测不再复杂

2017-05-27  本文已影响226人  街头客

内存泄漏的定义

​内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
内存泄漏形象的比喻是“操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以“内存泄漏”是从操作系统的角度来看的。这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄漏了。
​ ——来自《百度百科》

内存泄漏的影响

1,导致App的OOM(java.lang.OutOfMemoryError);

内存泄漏的类型

IO
----FileStream
----Cursor

Bitmap

Context
----单例
----Callback

Service
----BraodcastReceiver
----ContentObserver

Handler

Thread

Java不同的引用方式,垃圾回收机制的不同

Java引用

详细请参考:Java 7之基础 - 强引用、弱引用、软引用、虚引用。

怎么更有效的监控、解决内存泄漏的问题?

今天推荐一个第三方的库,LeakCanary。

LeakCanary

LeakCanary 是一个检测内存泄露的开源类库。你可以在 debug 包种轻松检测内存泄露。

写一个LeakCanary的DEMO

1,配置build.gradle文件,如下:

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'

2,创建Application类,如下:

package com.example.chenchubin.myleakcanary;

import android.app.Application;
import android.os.StrictMode;

import com.squareup.leakcanary.LeakCanary;

/**
 * 启动程序时,调用的类 2017/5/27 09:24
 */
public class App extends Application {

    private static App mInstance;

    public static App getInstance() {
        return mInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;

        if (LeakCanary.isInAnalyzerProcess(this)) {
            // This process is dedicated to LeakCanary for heap analysis.
            // You should not init your app in this process.
            return;
        }

        enabledStrictMode();
        LeakCanary.install(this); // 初始化 2017/5/27 09:24

    }

    /**
     * 启动Strict mode 2017/5/27 09:24
     * 主要检查项:内存泄露、耗时操作
     */
    private static void enabledStrictMode() {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy
                .Builder()
                .detectAll()
                .penaltyLog()
                .build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy
                .Builder()
                .detectAll()
                .penaltyLog()
                .build());
    }

}

PS:

其中使用了StrictMode(称为:严苛模式)。
StrictMode目前有两种策略,一种是线程策略,另一种是虚拟机策略。

线程策略

Disk Reads 磁盘读
  Disk Writes 磁盘写
  Network access 网络访问
  Custom Slow Code 自定义的运行速度慢的代码分析

前面三种的意思读者应该很清楚,就是正如它们的名字所示,分别对磁盘的读和写,网络访问进行监控。而第四种的自定义慢代码分析,是仅当访问调用类的时后才触发的,可以通过这种方法去监视运行缓慢的代码。当在主线程中调用时,这些验证规则就会起作用去检查你的代码。比如,当你的应用在下载或者解析大量的数据时,你可以触发自定义运行速度慢代码的查询分析,作用很大。StrictMode可以用于捕捉发生在应用程序主线程中耗时的磁盘、网络访问或函数调用,可以帮助开发者使其改进程序,使主线程处理UI和动画在磁盘读写和网络操作时变得更平滑,避免主线程被阻塞的发生。

虚拟机策略

内存泄露的Activity对象
  内存泄露的SQLite对象
  内存泄露的释放的对象

其中,内存泄露的Activity对象和内存泄露的SQLite对象都比较好理解,而所谓对关闭对象的检查,主要是去监那些本该释放的对象,比如应该调用close()方法的对象,如数据库的Cursor.close()对象。

Logcat中的表现

日志

3,创建TestLeakActivity类,如下:

package com.example.chenchubin.myleakcanary;


import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;

/**
 * 测试Leak 2017/5/27 09:29
 */
public class TestLeakActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                startAsyncTask();
            }
        });
    }

    void startAsyncTask() {
        System.out.println("==========000");

        /**
         * 在这个任务结束前,销毁该Activity类,并且重复执行足够多的该操作。 2017/5/27 09:48
         * PS: 打开该Activity,指定异步任务,finish。。。一直重复执行后,可以引发OOM。
         */
        new AsyncTask<Void, Void, Void>() {
            @Override protected Void doInBackground(Void... params) {
                // Do some slow work in background
                System.out.println("==========111");
                SystemClock.sleep(20000);
                System.out.println("==========222");
                return null;
            }
        }.execute();
    }
}

4,在MainActivity中调用,代码如下:

package com.example.chenchubin.myleakcanary;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, TestLeakActivity.class));
            }
        });
    }
}

5,运行结果

桌面生成Leak图标,如下:

桌面图标

按照上面的DEMO执行后,通知栏展示如下:

通知栏展示

Leaks展示如下:

Leaks列表 Leak详情

LeakCanary部分日志,如下:

日志

结尾

了解了内存泄漏的类型,引发的原因以及产生的影响,而且内存泄漏是一件长期、持续的事,所以应该早早的检测并对其进行解决。

希望上述能对你产生帮助。

上一篇下一篇

猜你喜欢

热点阅读