让内存泄漏检测不再复杂
内存泄漏的定义
内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
内存泄漏形象的比喻是“操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以“内存泄漏”是从操作系统的角度来看的。这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄漏了。
——来自《百度百科》
内存泄漏的影响
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部分日志,如下:
日志结尾
了解了内存泄漏的类型,引发的原因以及产生的影响,而且内存泄漏是一件长期、持续的事,所以应该早早的检测并对其进行解决。
希望上述能对你产生帮助。