一般情况下如果我想要较详细的分析内存状况,会使用android monitor(下简称monitor)和Memory Analyzer工具(下简称MAT),但步骤有些麻烦。
- 使用monitor dump堆文件
- 使用hprof-conv工具,将hprof转换成MAT能够识别的格式
- 使用MAT分析
需要手动dump hprof再使用hprof-conv工具手动转码,上篇Android Bitmap的内存大小是如何计算的?说到使用square的haha来做内存堆中Bitmap的分析。所以在思考如何一键完成内存dump和analyze的过程,当然也可以任意其他Java对象做分析。
如何完成dump和转码,思路还是Read the fucking source code,看下monitor是如何dump hprof文件的。打开Android sdk目录,monitor和其依赖的jar分别在/tools
和’/tools/lib’目录,核心代码在ddmlib.jar
中,Java swring代码在ddms.jar
和ddmuilib.jar
中,ddmlib需要依赖common.jar
和guava.jar
。
如何使用ddmlib获取adb连接的设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public static void main(String[] args) { IDevice device; // 这里需要传参true,不然待会会拿不到手机上的client AndroidDebugBridge.init(true); AndroidDebugBridge bridge = AndroidDebugBridge.createBridge( "/Users/Ivonhoe/Library/Android/sdk/platform-tools/adb", false); waitForDevice(bridge); IDevice devices[] = bridge.getDevices(); device = devices[0]; }
private static void waitForDevice(AndroidDebugBridge bridge) { int count = 0; while (!bridge.hasInitialDeviceList()) { try { Thread.sleep(100); count++; } catch (InterruptedException ignored) { } if (count > 300) { System.err.print("Time out"); break; } } }
|
如何dump hprof
这里不说看ddms源码的过程了,主要还是通过关键字搜索跟一下dump操作的处理逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| /** * 设置HprofDumpHandler,对dump事件做监听处理dump成功或者失败的回调, * 并保存hprof文件数据 * * @param device */ public HprofDump(Device device) { mDevice = device;
ClientData.IHprofDumpHandler hprofDumpHandler = new ClientData.IHprofDumpHandler() {
@Override public void onSuccess(String remoteFilePath, Client client) { String hprofPath = getHprofPath(client.getClientData().getClientDescription()); mDevice.pull(hprofPath, remoteFilePath);
conversionAndRemoveHprof(hprofPath);
isDumping = false; }
@Override public void onSuccess(byte[] bytes, Client client) { String hprofPath = getHprofPath(client.getClientData().getClientDescription()); Utilities.saveFile(bytes, hprofPath);
conversionAndRemoveHprof(hprofPath);
isDumping = false; }
@Override public void onEndFailure(Client client, String s) { isDumping = false; } }; ClientData.setHprofDumpHandler(hprofDumpHandler); }
|
这里默认选择栈顶进程做dump操作,通过adb shell dumpsys activity top命令获取栈顶TASK的ApplicationId,通过IDevice接口的getClient方法获取Client对象,执行dumpHprof操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| /** * dump栈顶进程的hprof */ public void dumpTopTaskHprof() { if (isDumping) { return; }
IDevice iDevice = mDevice.getIDevice();
/** * 这里自动选择手机上栈顶进程做dump操作,获取applicationName */ String topApp = getTopApplication(); Client client = iDevice.getClient(topApp);
if (client == null) { throw new RuntimeException("Can not dump app:" + topApp); } client.dumpHprof(); isDumping = true; }
|
在IHprofDumpHandler的回调中, 保存hprof文件,做格式转换,分析Bitmap对象的状况。BitmapAnalyzer.java的代码在上文说过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private void conversionAndRemoveHprof(String hprofPath) { String convHprofPath = getConvHprofPath(hprofPath); hprofConv(hprofPath, convHprofPath); removeFile(hprofPath);
analyzerBitmap(convHprofPath); }
private void hprofConv(String hprofPath, String convHprofPath) { String adbPath = Adb.instance().getAdbPath(); String hprofConvPath = adbPath.replace("adb", "hprof-conv"); ShellCommand.exec(hprofConvPath + " " + hprofPath + " " + convHprofPath); }
private void analyzerBitmap(String hprofPath) { String[] args = new String[2]; args[0] = hprofPath; args[1] = "bitmap"; BitmapAnalyzer.main(args); }
|