解密native代码的内存使用
前言
无论是从资源使用的角度,还是从发现内存泄漏问题的角度来看,在性能测试或者系统的稳定性测试中,内存的使用情况是一个很重要的监控点。为保证项目的质量前移,输入法内核测试小组的同学分配到了一个新的任务,就是在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!");
jstring类型是JNI提供的,对应于java的string类型。
JNI函数NewStringUTF()用于构造一个string对象,该对象存放在java heap中,同时返回了一个jstring类型的引用。
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代码的内存使用的更多相关文章
- Ngen生成Native代码实战及优缺点分析
先科普一下,.Net是一个用于Windows的托管代码模型,用于高效构建具有视觉上引人注目的用户体验的应用程序.但这个模型生成的代码并非可执行代码,而是由.Net公共语言运行库环境执行的IL代码.所以 ...
- Android Native 代码NDK开发学习笔记
引用:http://www.kunli.info/2011/08/21/android-native-code-study-note/ JNI,全称Java Native Interface,是用于让 ...
- Java实现MD5加密及解密的代码实例分享
链接:http://www.jb51.net/article/86027.htm Java实现MD5加密及解密的代码实例分享 作者:厦门大学陈黎栋 字体:[增加 减小] 类型:转载 时间:2016-0 ...
- 彻底告别加解密模块代码拷贝-JCE核心Cpiher详解
前提 javax.crypto.Cipher,翻译为密码,其实叫做密码器更加合适.Cipher是JCA(Java Cryptographic Extension,Java加密扩展)的核心,提供基于多种 ...
- Pythontutor:可视化代码在内存的执行过程
http://www.pythontutor.com/visualize.html今天去问开发一个Python浅拷贝的问题,开发给了一个神器,可以可视化代码在内存的执行过程,一看即懂,太NB了!~真是 ...
- Java native代码编译步骤简书
Java native代码编译步骤简书 目的:防止java代码反编译获取密码算法 (1)编写实现类com.godlet.PasswordAuth.java (2)编译java代码javac Passw ...
- c++ 汇编代码看内存分配
汇编代码看内存分配 (1). 程序运行时分为存储区域分为 存储区域 存储内容 extra 代码区 存放代码指令,包括除字符串常量的字面值 静态存储区 存放静态变量和全局变量 执行main之前就分配好了 ...
- 如何用Java编写一段代码引发内存泄露
本文来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码. Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码.这个问题我一点思路都没有, ...
- 怎样用Java编写一段代码引发内存泄露
通过下面步骤能够非常easy产生内存泄露(程序代码不能訪问到某些对象,可是它们仍然保存在内存中): 应用程序创建一个长时间执行的线程(或者使用线程池,会更快地发生内存泄露). 线程通过某个类载入器(能 ...
随机推荐
- 微信小程序学习二 微信小程序的项目结构
进来之后可以看到五个文件和两个文件夹,一般新建的小程序项目都是这种格式,但有些可能会不一样,不用担心,因为我们所要关注的文件是不会变的 pages 小程序的页面放置文件夹,每一个页面(page)包含四 ...
- Halo(十)
Spring Converter(转换器) @FunctionalInterface public interface Converter<S, T> { //一对一转换 @Nullabl ...
- pgrep,pidof工具的使用
博客pgrep,pidof工具的使用 最灵活:ps 选项 | 其它命令 按预定义的模式:pgreppgrep [options] pattern-u uid: effective user,生效者-U ...
- SpringBoot编程思想
Spring Boot的特性 1).创建独立的Spring应用 2).直接嵌入Tomcat.Jetty或Undertow等Web容器(不需要部署WAR文件) 3).提供固化的starter依赖,简化构 ...
- 「长乐集训 2017 Day8」修路 (斯坦纳树)
题目描述 村子间的小路年久失修,为了保障村子之间的往来,AAA君决定带领大家修路. 村子可以看做是一个边带权的无向图GGG, GGG 由 nnn 个点与 mmm 条边组成,图中的点从 1∼n1 \si ...
- hdu1166:敌兵布阵(树状数组或线段树)
题目描述: 一堆废话不用看...... 输入: 第一行一个整数T,表示有T组数据.每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表 ...
- signer information does not match signer information of other classes in the same package
报错日志: java.lang.SecurityException: class "org.bouncycastle.asn1.ASN1ObjectIdentifier"'s si ...
- java配置和tomcat安装
原文: https://www.cnblogs.com/lwjboke/p/7089126.html 下载: jdk历史版本 下载地址: http://www.oracle.com/technetwo ...
- 神秘的java Https
说起网络安全,最基本的策略就是走https.https仿佛一条神秘通道,有了它,万事无忧. 究竟什么是https?如何实现https? 本文将揭开https的神秘面纱. WTF https 万事皆有源 ...
- 购物车2.0版——python第6天
li = [{'}, {'}, {'}, {'}, {'}, ] shopping_car = {} # 定义购物车dict print('欢迎光临尚雅梦想旗舰店'.center(40)) # 先让顾 ...