1. LeakCanary 检查内存泄露

LeakCanary 是一个开源的在debug版本中检测内存泄漏的java库。

让我们来看看一个cait的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Cat {
}
class Box {
  Cat hiddenCat;
}
class Docker {
  static Box container;
}
 
// ...
 
Box box = new Box();
Cat schrodingerCat = new Cat();
box.hiddenCat = schrodingerCat;
Docker.container = box;

创建一个RefWatcher实例,然后给它一个对象让它观察:

1
2
// We expect schrodingerCat to be gone soon (or not), let's watch it.
refWatcher.watch(schrodingerCat);

当检测出泄漏的时候,你会自动得到一个漂亮的泄漏线索:

1
2
3
* GC ROOT static Docker.container
* references Box.hiddenCat
* leaks Cat instance

我们知道你的时间宝贵,因此我们让它非常好设置。只需几行代码,LeakCanary就能自动检测Activity的泄漏:

1
2
3
4
5
6
public class ExampleApplication extends Application {
  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}

当内存不足时,会有一个通知和良好的展示界面:

结论

在启用LeakCanary之后,我们发现和修复了许多内存泄漏的问题。我们甚至发现了一些Android SDK中的泄漏

结果是非常令人惊奇的,我们现在减少了94%的oom崩溃问题。

2.BlockCanary 检查ui卡顿

BlockCanary对主线程操作进行了完全透明的监控,并能输出有效的信息,帮助开发分析、定位到问题所在,迅速优化应用。其特点有:

  • 非侵入式,简单的两行就打开监控,不需要到处打点,破坏代码优雅性。
  • 精准,输出的信息可以帮助定位到问题所在(精确到行),不需要像Logcat一样,慢慢去找。

目前包括了核心监控输出文件,以及UI显示卡顿信息功能。仅支持Android端。

原理

熟悉Message/Looper/Handler系列的同学们一定知道 Looper.java 中这么一段:

private static Looper sMainLooper;  // guarded by Looper.class

...

/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
} /** Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

即整个应用的主线程,只有这一个looper,不管有多少handler,最后都会回到这里。

如果再细心一点会发现在Looper的loop方法中有这么一段

public static void loop() {
... for (;;) {
... // This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
} msg.target.dispatchMessage(msg); if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
} ...
}
}

是的,就是这个Printer - mLogging,它在每个message处理的前后被调用,而如果主线程卡住了,不就是在dispatchMessage里卡住了吗?

核心流程图:

该组件利用了主线程的消息队列处理机制,通过

Looper.getMainLooper().setMessageLogging(mainLooperPrinter);

并在 mainLooperPrinter 中判断start和end,来获取主线程dispatch该message的开始和结束时间,并判定该时间超过阈值(如2000毫秒)为主线程卡慢发生,并dump出各种信息,提供开发者分析性能瓶颈。

...
@Override
public void println(String x) {
if (!mStartedPrinting) {
mStartTimeMillis = System.currentTimeMillis();
mStartThreadTimeMillis = SystemClock.currentThreadTimeMillis();
mStartedPrinting = true;
} else {
final long endTime = System.currentTimeMillis();
mStartedPrinting = false;
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
}
} private boolean isBlock(long endTime) {
return endTime - mStartTimeMillis > mBlockThresholdMillis;
}
...

说到此处,想到是不是可以用mainLooperPrinter来做更多事情呢?既然主线程都在这里,那只要parse出app包名的第一行,每次打印出来,是不是就不需要打点也能记录出用户操作路径? 再者,比如想做onClick到页面创建后的耗时统计,是不是也能用这个原理呢? 之后可以试试看这个思路(目前存在问题是获取线程堆栈是定时3秒取一次的,很可能一些比较快的方法操作一下子完成了没法在stacktrace里面反映出来)。

功能

BlockCanary会在发生卡顿(通过MonitorEnv的getConfigBlockThreshold设置)的时候记录各种信息,输出到配置目录下的文件,并弹出消息栏通知(可关闭)。

简单的使用如在开发、测试、Monkey的时候,Debug包启用

  • 开发可以通过图形展示界面直接看信息,然后进行修复
  • 测试可以把log丢给开发,也可以通过卡慢详情页右上角的更多按钮,分享到各种聊天软件(不要怀疑,就是抄的LeakCanary)
  • Monkey生成一堆的log,找个专人慢慢过滤记录下重要的卡慢吧

还可以通过Release包用户端定时开启监控并上报log,后台匹配堆栈过滤同类原因,提供给开发更大的样本环境来优化应用。

本项目提供了一个友好的展示界面,供开发测试直接查看卡慢信息(基于LeakCanary的界面修改)。

dump的信息包括:

  • 基本信息:安装包标示、机型、api等级、uid、CPU内核数、进程名、内存、版本号等
  • 耗时信息:实际耗时、主线程时钟耗时、卡顿开始时间和结束时间
  • CPU信息:时间段内CPU是否忙,时间段内的系统CPU/应用CPU占比,I/O占CPU使用率
  • 堆栈信息:发生卡慢前的最近堆栈,可以用来帮助定位卡慢发生的地方和重现路径

sample如下图,可以精确定位到代码中哪一个类的哪一行造成了卡慢。

总结

BlockCanary作为一个Android组件,目前还有局限性,因为其在一个完整的监控系统中只是一个生产者,还需要对应的消费者去分析日志,比如归类排序,以便看出哪些卡慢更有修复价值,需要优先处理;又比如需要过滤机型,有些奇葩机型的问题造成的卡慢,到底要不要去修复是要斟酌的。扯远一点的话,像是埋点除了统计外,完全还能用来做链路监控,比如一个完整的流程是A -> B -> D -> E, 但是某个时间节点突然A -> B -> D后没有到达E,这时候监控平台就可以发出预警,让开发人员及时定位。很多监控方案都需要C/S两端的配合。

目前阿里内多个Android项目接入并使用BlockCanary来优化Android应用的性能。

3. MAT 内存溢出 http://blog.csdn.net/aaa2832/article/details/19419679

4. 过度绘制 - 开发者模式里面的“过度绘制”  http://www.cnblogs.com/liuling/p/2015-10-08-2.html

android 内存处理工具的更多相关文章

  1. Android 内存监测工具 DDMS --> Heap

    一.什么是内存泄露    内存泄露是指程序中间动态分配了内存,但是在程序结束时没有释放这部分内存,从而造成那一部分内存不可用.导致系统运行变慢或应用程序崩溃.二.如何检测Android中的内存泄露   ...

  2. Android 内存分析工具 MAT(Memory Analyzer Tool)

    如果使用DDMS确实发现了我们的程序中存在内存泄漏,那又如何定位到具体出现问题的代码片段,最终找到问题所在呢?如果从头到尾的分析代码逻辑,那肯定 会把人逼疯,特别是在维护别人写的代码的时候.这里介绍一 ...

  3. Android内存分析工具DDMS heap + MAT 安装和使用

    一  Java内存分析工具扫盲 如果像我一样一点都不了解,可以先进行内存分析工具扫盲   MAT介绍:     Eclipse Memory Analyzer(MAT)一个功能丰富的 JAVA 堆转储 ...

  4. Android内存分析工具

    在Android系统开发过程中,经常会要去分析进程的内存的使用情况,简单介绍下Android内存分析的相关工具. 文章参考: 1.dumpsys 2.memory-analysis-command 1 ...

  5. Android 内存监测工具

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 文/幻影浪子 [博主导读]俗话说:工欲善其事必先利其器!我们先来了解下内存监测工具是怎么使用的?为内 ...

  6. Android内存监测工具使用

    用 Heap监测应用进程使用内存情况的步骤如下:1. 启动eclipse后,切换到DDMS透视图,并确认Devices视图.Heap视图都是打开的:2. 将手机通过USB链接至电脑,链接时需要确认手机 ...

  7. Android 内存分析工具 - LogCat GC

    一.GC_Reason 触发垃圾回收的回收的集中原因: 类型 描述 GC_CONCURRENT 内存使用将满时,并发的进行垃圾回收. GC_FOR_MALLOC 当内存已满应用尝试分配内存时会出触发垃 ...

  8. 转: android 内存检测工具 LeakCanary 说明

    http://www.liaohuqiu.net/cn/posts/leak-canary-read-me/ LeakCanary 中文使用说明 10 May 2015 LeakCanary Andr ...

  9. Android 内存监测工具 DDMS --> Heap(转)

    DDMS 的全称是Dalvik Debug Monitor Service,它为我们提供例如:为测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息.Logcat.广播状态信息.模拟电话呼叫.接收 ...

随机推荐

  1. windows安装django

    Window 下安装 Django 如果你还未安装Python环境需要先下载Python安装包. 1.Python 下载地址:https://www.python.org/downloads/ 2.D ...

  2. asl 和 lgpl的区别

    按照使用条件的不同,开源软件许可证可以分为三类(严苛程度递减) 1. 使用该开源软件的代码再散布(redistribute)时,源码也必须以相同许可证公开. 代表许可类型:GPL, AGPL 2. 使 ...

  3. sprin加载顺序

    spring加载有个比较有意思的问题,这里片很不错的文章 http://guoliangqi.iteye.com/blog/632697

  4. 类 class

    <?php class Person{ //定义一个类 public $name; //定义属性 public $age; function say(){ //定义办法 echo "m ...

  5. BizTalk 开发系列(四十一) BizTalk 2010 BAM 安装手记

    使用64位系统可以支持更大的内存,现在服务器基本上都使用64位系统.微软从Windows Server 2008 R2开始服务器版的操作系统也只支持64位了,不过对于像BizTalk这种“繁杂的东西” ...

  6. JS开发windows phone8.1系列之3

    http://msdn.microsoft.com/zh-cn/library/windows/apps/dn629638.aspx 这部分主要是使用页面导航 管理方式,在程序的default.htm ...

  7. IOS第13天(1,私人通讯录,登陆功能,界面的跳转传值,自定义cell,编辑界面)

    ******HMLoginViewController 登陆的界面 #import "HMLoginViewController.h" #import "MBProgre ...

  8. 使用本地phpmyadmin管理远程数据库

    使用放下挺简单的,因为某些业务需求,phpmyadmin不让放在网站服务器上,所以放在本地可以方便管理远程数据库 点击下载会下载一个  config.inc.php 吧这个文件放在phpmyadmin ...

  9. .net MVC简介、项目中每个文件夹的功能

    MVC是微软2009对外公布的第一个开源的表示层框架,这是微软的第一个开源项目 M:viewmodel  V:视图  c:控制器 App_Data:一个比较特殊的文件夹,把文件放到这个文件夹,通过地址 ...

  10. Web前端开发规范文档

    Web前端开发规范文档 规范目的: 使开发流程更加规范化. 通用规范: TAB键用两个空格代替(windos下tab键占四个空格,linux下TAB键占八个空格). CSS样式属性或者JAVASCRI ...