前言

无论是从资源使用的角度,还是从发现内存泄漏问题的角度来看,在性能测试或者系统的稳定性测试中,内存的使用情况是一个很重要的监控点。为保证项目的质量前移,输入法内核测试小组的同学分配到了一个新的任务,就是在andriod平台上监测输入法内核 native 代码的内存使用情况。

为了完成这个任务,我们先将任务拆解成重要的几步:

1. 了解 andriod 平台的程序与内存

2. 了解 native代码在andriod平台上是如何进行内存分配的

3.  根据2步骤,方法调研,监测native代码内存使用情况

完成任务前先了解一些基本小知识:

堆和栈:

Stack空间(进栈和出栈)由操作系统控制,其中主要存储函数地址、函数参数、局部变量等等,所以Stack空间不需要很大,一般为几mb大小。

Heap空间 由程序控制,程序员可以使用malloc、new、free、delete等函数调用来操作这片地址空间。heap为程序完成各种复杂任务提供内存空间,所以空间比较大,一般为几百mb到几gb。正是因为heap空间由程序员管理,所以容易出现使用不当导致严重问题,我们重点关注的也是这部分由程序员控制的内存空间。‍

正文

1

了解 andriod 平台的程序与内存

在Android里,程序内存被分为2部分:native进程和:dalvik进程

native进程:采用c/c++实现,不包含dalvik实例的linux进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的。

java进程(dalvik进程):是实例化了dalvik虚拟机实例的linux进程,进程的入口main函数为java函数。dalvik虚拟机实例的宿主进程是fork()系统调用创建的linux进程,所以每一个android上的java进程实际上就是一个linux进程,只是进程中多了一个dalvik虚拟机实例。

使用命令 adb shell prorank 可以看到

/system/bin/* 下面的是native进程,桌面、电话、联系人、状态栏等等都是java进程。

在这里不详细描述,native进程和java进程究竟如何分配的内存,有兴趣的同学可以自行搜索,我们只重点关注两种进程空间中的heap空间的分配情况。heap空间完全由程序员控制分配,一共可分为dalvik heap 和 native heap两种,我们使用的malloc、c++ new和java new所申请的空间都是heap空间, c/c++申请的内存空间在native heap中,而java申请的内存空间则在dalvik heap中。

2

了解 native代码在andriod上是如何进行内存分配的

根据以上,我们已经了解了 c/c++代码申请的内存空间在native heap中,而java代码申请的内存空间则在dalvik heap中,那么 native代码在andriod平台上进行内存分配的这个过程是如何执行的呢,这就离不开一个概念JNI,什么是JNI:JNI是Java Native Interface的缩写,提供了若干API实现了java和其他语言的通信(主要是c&c++)。

在java代码中,java对象被存放在jvm的java heap中,由垃圾回收器自动回收就可以。

在JNI代码中,JNI与java的分配方式有所不同,JNI是java/其他语言交流的媒介。JNI提供了与java相对应的引用类型(如:jobject、jstring、jclass、jarray、jintArray等),以便native代码可以通过JNI函数访问到java对象。引用所指向的java对象通常就是存放在java heap,而native代码持有的引用是存放在native memory中。

举个例子,如下代码:

jstring jstr = env->NewStringUTF("Hello World!");

  1. jstring类型是JNI提供的,对应于java的string类型。

  2. JNI函数NewStringUTF()用于构造一个string对象,该对象存放在java heap中,同时返回了一个jstring类型的引用。

  3. string对象的引用保存在jstr中,jstr是native的一个局部变量,存放在native memory中。‍

在native代码中,根据下图jvm的内存模型,可以看到本地库接口是在在jvm运行时数据区域之外。因此,调用c/c++写的库,malloc 分配的内存是按照c/c++的规范去操作内存,内存是从native heap中分配的。

3

根据2步骤,方法调研,监测native代码内存使用情况

根据2步骤,我们可以知道,如果想在andriod平台监控native的内存占用情况,我们需要在native代码运行时监控native heap的分配情况。

使用命令adb shell dumpsys meminfo就可以达到以上需求:

一般实时输出的信息像这样:

在这里对主要字段进行解释:

横轴:

pss: proportional set size是内核计算的度量,代表整体的内存情况,它将内存共享考虑在内。系统会根据内存页中的其他进程使用的比例动态调整每个RAM内存页。

private dirty:进程独占的内存。也就是应用进程销毁时系统可以直接回收的内存容量。通常来说,“private dirty”内存是其最重要的部分,因为只被自己的进程使用。它只在内存中存储,因此不能做分页存储到外存。所有分配的dalvik堆和本地堆都是“private dirty”内存。pss/private dirty三列是读取了/proc/process-id/smaps文件获取的,可以通过adbshell cat /proc/process-id/smaps来查看。

private clean:包括该进程私有的干净的内存。

纵轴:

dalvik heap:应用中dalvik分配使用的内存。

native heap:应用中c/c++分配使用的内存。

.so / jar /apk /...mmap:c库 / java /apk /...代码占用的内存。

unknown:无法归类到其它项的内存页。目前,这主要包含大部分的本地分配,就是那些在工具收集数据时由于地址空间布局随机化不能被计算在内的部分。

total:进程总使用的实际使用内存(PSS),是上面所有PSS项的总和。它表明了进程总的内存使用量,可以直接用来和其它进程或总的可以内存进行比较。

结论

使用命令adb shell dumpsys meminfo 监控native heap只能大致分析native 代码的使用情况,与真实使用存在一定的误差,如果对native内存使用有着非常高精确的需求,可以尝试抓取一个带有调用堆栈地址的native heap usage, 有了堆栈地址, 我们可以通过arm-linux-androideabi-addr2line.exe把地址翻译成函数名. 尝试将堆栈地址合并进行内存的累加统计,可以精确的看到函数的内存占用情况,不过小编还没有尝试过这种方法,如果有尝试过的同学,欢迎在评论里和大家分享经验,不胜感谢~

解密native代码的内存使用的更多相关文章

  1. Ngen生成Native代码实战及优缺点分析

    先科普一下,.Net是一个用于Windows的托管代码模型,用于高效构建具有视觉上引人注目的用户体验的应用程序.但这个模型生成的代码并非可执行代码,而是由.Net公共语言运行库环境执行的IL代码.所以 ...

  2. Android Native 代码NDK开发学习笔记

    引用:http://www.kunli.info/2011/08/21/android-native-code-study-note/ JNI,全称Java Native Interface,是用于让 ...

  3. Java实现MD5加密及解密的代码实例分享

    链接:http://www.jb51.net/article/86027.htm Java实现MD5加密及解密的代码实例分享 作者:厦门大学陈黎栋 字体:[增加 减小] 类型:转载 时间:2016-0 ...

  4. 彻底告别加解密模块代码拷贝-JCE核心Cpiher详解

    前提 javax.crypto.Cipher,翻译为密码,其实叫做密码器更加合适.Cipher是JCA(Java Cryptographic Extension,Java加密扩展)的核心,提供基于多种 ...

  5. Pythontutor:可视化代码在内存的执行过程

    http://www.pythontutor.com/visualize.html今天去问开发一个Python浅拷贝的问题,开发给了一个神器,可以可视化代码在内存的执行过程,一看即懂,太NB了!~真是 ...

  6. Java native代码编译步骤简书

    Java native代码编译步骤简书 目的:防止java代码反编译获取密码算法 (1)编写实现类com.godlet.PasswordAuth.java (2)编译java代码javac Passw ...

  7. c++ 汇编代码看内存分配

    汇编代码看内存分配 (1). 程序运行时分为存储区域分为 存储区域 存储内容 extra 代码区 存放代码指令,包括除字符串常量的字面值 静态存储区 存放静态变量和全局变量 执行main之前就分配好了 ...

  8. 如何用Java编写一段代码引发内存泄露

    本文来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码. Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码.这个问题我一点思路都没有, ...

  9. 怎样用Java编写一段代码引发内存泄露

    通过下面步骤能够非常easy产生内存泄露(程序代码不能訪问到某些对象,可是它们仍然保存在内存中): 应用程序创建一个长时间执行的线程(或者使用线程池,会更快地发生内存泄露). 线程通过某个类载入器(能 ...

随机推荐

  1. BZOJ3508 开灯 & [校内NOIP2018模拟20181027] 密码锁

    Time Limit: 10 Sec Memory Limit: 128 MB Description xx作为信息学界的大神,拥有众多的粉丝.为了感谢众粉丝的爱戴,xx决定举办一场晚会.为了气派,x ...

  2. cornerNet部分学习内容记录

    cornerNet来源灵感是基于多人姿态估计的从下往上思想,预测角的热图,根据嵌入式向量对角进行分组,其主干网络也来自于姿态估计的环面网络. cornerNet的总体框架结构图如下:  CornerN ...

  3. orcad原理图与PSPICE模型库名称

    Vendor PSpice Model Description Advanced Linear Devices adv_lin.lib Library of op-amps Advanced Line ...

  4. 人生苦短_我用Python_javascript_var_function_简单笔记_001

    <!--Javascript_var_001:--> <html> <head> <meta charset="UTF-8"> &l ...

  5. .NET面试题集锦①

    一.前言部分 文中的问题及答案多收集整理自网络,不保证100%准确,还望斟酌采纳. 1.面向对象的思想主要包括什么? 答:任何事物都可以理解为对象,其主要特征: 继承.封装.多态.特点:代码好维护,安 ...

  6. 项目中有 xxxx 不能被json序列化

    遇到这类问题 ,首先断点调试,看看要序列化的值 是一个什么类型的值 查看值得数据类型 在将值转化成可以被json序列化的对象 此时即可解决问题 如遇到  requests.post() 朝一个url发 ...

  7. k-近邻算法(kNN)测试算法:作为完整程序验证分类器

    #测试算法:作为完整程序验证分类器 def datingClassTest(): hoRatio = 0.10 #设置测试集比重,前10%作为测试集,后90%作为训练集 datingDataMat,d ...

  8. Python基础教程(021)--Pycharm简介

    前言 学习Pycharm开发工具 内容 项目:就是一个功能复杂的软件 目标 必须掌握的工具

  9. spring+cxf

    里面有http://127.0.0.1:8081/dcs/soap/cls       http://127.0.0.1:8081/dcs/soap/cms       http://127.0.0. ...

  10. mybatis批量生成

    使用了mybatis-generator后,寻找只写一个table标签就可以全部生成的方法 下载mybatis-generator-core-1.3.2-bundle.zip 解压后打开docs 发现 ...