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. 读《深入php面向对象、模式与实践》有感(三)

    命令模式: 第一次接触到这个命令模式的时候,感觉它很像一个简化的mvc框架.从不同的路径访问,再由控制器来判断所要调用的具体php文件. <?php class CommandContext{ ...

  2. 关于Jquery获取Table中td内的内容

    $(this).children().eq(1).text()获取的是显示的值$(this).children().eq(1).html()获取的是<td></td>之间的所有 ...

  3. acm常见算法及例题

    转自:http://blog.csdn.net/hengjie2009/article/details/7540135 acm常见算法及例题  初期:一.基本算法:     (1)枚举. (poj17 ...

  4. js 时间格式化 -- 时间加减实现

    时间格式化的方法: Date.prototype.Format = function (fmt) { //author: meizz var o = { "M+": this.ge ...

  5. SpringBoot项目部署与服务配置

    spring Boot 其默认是集成web容器的,启动方式由像普通Java程序一样,main函数入口启动.其内置Tomcat容器或Jetty容器,具体由配置来决定(默认Tomcat).当然你也可以将项 ...

  6. Eclipse序列号生成代码

    import java.io.*; public class MyEclipseGen { private static final String LL = "Decompiling thi ...

  7. BizTalk动手实验(九)业务规则引擎使用

    1 课程简介 通过本课程熟悉业务规则引擎(BRE)的使用(本环境为Windows 2008 32位操作系统环境 + Visual Studio 2010 + BizTalk 210) 2 准备工作 1 ...

  8. Android课程---时间日期对话框

    activity_ui2.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout x ...

  9. IOS第四天(1:图片的方法和缩小,遮罩层)

    @interface HMViewController () @property (nonatomic, strong) UIButton *cover; //阴影 @end @implementat ...

  10. 【iCore3 双核心板_FPGA】例程二:GPIO输出实验——点亮三色LED

    实验指导书及代码包下载: http://pan.baidu.com/s/1boiOJBT iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...