• 孙鹏飞 置顶
    2018-12-07
    更新了例子,完善了操作方式,支持了x86平台的编译运行,兼容7.1到9.0的手机和模拟器,支持x86和armv7a,已经相对稳定

    作者回复: 鹏飞棒棒的!

    
     28
  • 任雪龙
    2018-12-06
    厉害,很厉害,但是对于普通公司的应用来说是不是用不到呢,还是说我们这些普通开发者并没有研究到这个层次。
    
     24
  • csdpz
    2018-12-06
    国民app的开发者知识面真是好宽广
    
     18
  • 奔流河
    2018-12-07
    感觉自己太菜了,看的一脸懵逼。老师 ,可以循序渐进一些吗?一开始就从底层开始 ,听不太懂啊......
    
     16
  • 旁友💊有伐🤔
    2018-12-06
    兴致勃勃地去把sample下载下来想要拜读一下,发现是JNI里面看的我一脸懵逼

    作者回复: 可以顺着sample去看一下hook点,为什么这样hook。然后去看看allocate 监控的源码,不一定需要看懂sample的实现

    
     14
  • 周迷奇
    2018-12-06
    “一张图,毁十优“,之前专门做过一段时间性能优化工作,内存就是优化的重点,除了解决内存泄漏,减少内存的申请,及时回收,减少常驻内存,精简布局外,图片也做了专项整治,依据图片所占内存大小=长×宽/(资源图片文件密度/手机屏幕密度)∧2×每个像素占用字节数的大体相关性,从这几个维度下手,各个击破,还是收获不小。当然如张老师所言,我以后会多考虑怎么提高内存的使用效率上,而不是单纯的想着怎么减小内存使用的绝对值上,涨姿势了,感谢,加油!
    
     9
  • 海贼凯
    2018-12-10
    做应用层久了,好多东西都没去深入研究,看了这几篇,好多都没用过,深感惭愧,不过从另一方面想,这也是一个提升自己的机会,加油努力!
    
     6
  • 菜而已
    2018-12-24
    能不能讲点实例,这种纯理论,让人一脸懵逼啊。
    
     5
  • gufan
    2018-12-06
    正是我需要的教程,感谢张老师
    
     5
  • 李华
    2019-10-31
    我在电脑前,你也在电脑前,我沉默,你也不说话,但你用这篇专栏嘲笑了我。

    作者回复: 不要嘲笑自己就行,一步一步向前

    
     4
  • 小鹏
    2018-12-06
    大佬,越讲越难了

    编辑回复: 高手课,本身知识点就难一些哈,如果觉得哪里吃力,可以评论区提问。

    
     4
  • 王洛民
    2019-01-19
    学习起来很吃力,文章好多都看不懂,现在在处理内存相关的问题,可以告知下都需要掌握哪些基础的知识吗?系统学习的,多谢多谢

    作者回复: 后面会统一的来讲

    
     2
  • 朱蓝天
    2018-12-11
    https://www.jianshu.com/p/f6386d33062b,这个同学写的基于线上用户的内存占用分析也很不错,对leakcanery进行了改造,并对hprof进行本地解析,在上传到服务端做聚合。
    
     2
  • HI
    2018-12-10
    通过查看JNI代码,学到了 JNI中CmakeList的跨模块编译,native 函数名的简写
    #define JNI_METHOD_DECL(ret_type, method_name) \
         extern "C" JNIEXPORT ret_type JNICALL Java_##com_dodola_alloctrack##_##AllocTracker##_##method_name

    以及 Substrate,fbjni ,ndk_dlopen 库的使用,对于源码中的Substrate 库的编译是自己从源码中抽取出来的,貌似没有在github上面看到
    展开
    
     2
  • 庆
    2019-12-23
    看了一天代码和相关资料,终于算搞清楚老师想要教啥了,其实多查阅点资料就行,虽然的确很多JNI的东西,但是多花点时间也可以搞懂。 分享一下心得:

    前提知识:
    1. Java可以用反射来hook,C++没有反射但是同样可以hook。MSHookFunction 就是这样一个框架,支持hook C/C++代码, http://www.cydiasubstrate.com/api/c/MSHookFunction/

    2.ndk_dlopen 和 ndk_dlsym 。前者是用来获取动态链接库,后者是通过获取到前者的动态链接库之后,获取函数地址的。

    3.C/C++在链接之前会把函数的名字mangled, 我们在ndk_dlsym函数里面需要填mangled之后的函数名, -》 _ZN3art3Dbg21DumpRecentAllocationsEnv

    4. facebook::jni::这个应该是调用文章中说到的那个facebook的jni的开源框架。 JNIEnv *env 这个是一个全局的JNI环境变量,储存了变量,还有很多JNI的函数供开发者使用。比如代码中的env->GetArrayLength(saveData.data);

    代码流程:

    1. 首先先调用了tracker.initForArt, 并且调用了JNI方法initForArt。所谓的初始化其实就是使用ndk_dlsym 拿到各个要hook的函数。比如artSetAllocTrackingEnable ,就是开启/关闭tracking的,源代码中有一行:

      artSetAllocTrackingEnable = (void (*)(bool)) ndk_dlsym(libHandle,
                                                               "_ZN3art3Dbg23SetAllocTrackingEnabledEb");

    这里注意并没有开启函数,只是拿到函数句柄而已。

    2. void hookFunc() , 这个方法就是真正的把系统的tracking 函数hook调的地方。

    比如 先调用 void *hookRecordAllocation26 = ndk_dlsym(handle,
                                                 "_ZN3art2gc20AllocRecordObjectMap16RecordAllocationEPNS_6ThreadEPNS_6ObjPtrINS_6mirror6ObjectEEEj");

    这里注意是拿到函数的地址而不是句柄。

    然后 调用hook -> MSHookFunction(hookRecordAllocation26, (void *) &newArtRecordAllocation26,
                           (void **) &oldArtRecordAllocation26);
    通过这个函数把newArtRecordAllocation26 hook进原函数地址里,同时拿到旧函数的实现并导向oldArtRecordAllocation26。保留旧函数的原因是还需要使用旧函数的一些功能。

    3.最后我们可以看到hook的新函数,做了一个大小的判断。 if (allocObjectCount > setAllocRecordMax), 如果大于setAllocRecordMax,就
          jbyteArray allocData = getARTAllocatio nData();
            SaveAllocationData saveData{allocData};
            saveARTAllocationData(saveData);

    以上代码就是把数据保存在log文件里面的实现。


    总结:

    其实总结下来,这个小作业我觉得重点在于理解在哪里hook,还有认识到不同的ROM版本的小差异在哪,毕竟hook和java反射一样,如果系统在新版本把函数名,或者函数签名改了那肯定就凉凉了。 最后,把这一套hook如果应用在生产环境里,可以实现一些自动化的分析app的内存消耗,不需要再手动调用profiler。我觉得这堂课受益匪浅,可以考虑自己整一个然后给QA的automation lab用。

    最后老师我有一个问题,你是怎么知道C++函数的mangled name的?我看网上都说是compile time才能知道,难道你为了这个专门compile了一下android 的源代码?

    谢谢!
    展开
    
     1
  • 镜像
    2019-05-07
    说一下今天作业的遇到的问题和解决,希望可以帮助到大家。
    1.界面中的输出内存DUMP到日志,说的是把日志写入到Logcat的中并不是输入。
    2.java代码中 new File(Environment.getExternalStorageDirectory(), "crashDump"); 是日志输出的文字,我们看Logcat中 saveARTAllocationData write file to XXXX 就能找到对应的日志文件,和git上说的路径可能不一致。
    3.评论中有同学说看不到日志的输出。代码中 【tracker.initForArt(BuildConfig.VERSION_CODE, 5000);//从 start 开始触发到5000的数据就 dump 到文件中】 有说明开始后到达5000的数据才会写入文件。 大家设备内存情况不一样,GC回收的频率也不一致,在你不停生产1000个对象的时候,GC不断的跟随回收,导致无法达到 5000的数据量,所以一直没有日志的写入。 可以尝试修改对象的创建数量改成10000。
       
    展开

    作者回复: 赞

    
     1
  • 羽
    2018-12-29
    android 4.4.2手机 点击开始记录就crash,无法生成dump日志,利用第一课的native crash定位方式,报错位置是NDK14提供的库函数,换成NDK17还是库函数问题。使用android8.0手机成功。
    
     1
  • $$$
    2018-12-19
    老师好,adb shell kill -S QUIT PID 这个命令,执行了一下,有几点实践记录如下:
    1、其中“-S”文中是大写,实际执行是小写才行“-s”,查看adb shell kill的命令帮助如下:
    usage: kill [-s signame | -signum | -signame] { job | pid | pgrp } ...
            kill -l [exit_status ...]
    2、我用了一台 小米5 Android 7.0 一台 三星S8 Android 8.0 和一台华为荣耀3 Android 4.4.2 没root的三台机器执行这个命令会提示kill: PID : Operation not permitted;
    3、用了一个模拟器执行就可以
    展开

    作者回复: 这个其实也可以在代码中给自己发信号,这样就没有权限问题。

    可以看第六篇的补充篇

    
     1
  • 无知
    2018-12-14
    这是第三个了,没有一个能跑起来的。各种懵呀。

    作者回复: 可以看一下后面的例子答疑

    
     1
  • 的的喀喀湖
    2018-12-08
    谢谢老师,之前把理论基础过了一遍,一直不知道怎么实操,看完之后有了新的思路实操了
    
     1
我们在线,来聊聊吧