文档章节

Android内存分析和调优(上)

SuShine
 SuShine
发布于 2015/06/24 13:54
字数 2034
阅读 26
收藏 0
点赞 0
评论 0
最近我们的android app占用了大量内存,于是领导安排做减少内存占用的工作。
要优化内存,首先要做的就是分析内存占用情况。android提供了多个工具和命令进行内存分析。
 

第一层 Procrank

 
很粗略的,可以使用"adb shell procrank",结果类似于

PID    Vss        Rss        Pss       Uss      cmdline

......
2319 42068K 42032K 13536K 7028K com.xxx
......

该命令可以列出当前系统所有进程的内存占用情况。
PID是进程ID。
Vss是占用的虚拟内存,如果没有映射实际的内存也算进来。
Rss是占用的物理内存。是共享内存+私有内存。因为共享内存是多个进程共用的,所以存在重复计算。
Pss是占用的私有内存加上平分的共享内存。例如一块1M的共享内存被两个进程共享,那每个进程分500K。各进程的Pss相加基本等于实际被使用的物理内存,所以这个经常是最重要的参数。
Uss是私有内存。
cmdline可以看做是apk包名。

通过procrank,只能很宏观的横向比较不同的应用。如果要更细致的了解具体内存是如何使用,则需要进入

第二层 dumpsys meminfo

命令“adb shell dumpsys meminfo package.name”。在4.0 ICS(或者3.0 HoneyComb)之后的系统上,会看到类似下面的输出

                                 Shared   Private  Heap    Heap     Heap
                      Pss      Dirty      Dirty     Size     Alloc      Free
                      ------   ------    ------     ------  ------     ------
Native            16        8           16        3416   3300     79
Dalvik            3884    10592   3580    9560   9022     538
Cursor            0          0           0 
Ashmem        0           0           0 
Other dev       5110    10244   0 
.so mmap       640     1948      396 
.jar mmap      0          0           0 
.apk mmap     68        0           0 
.ttf mmap       817      0           0 
.dex mmap     411      0           0 
Other mmap   55        16         32 
Unknown        2404     660       2388 
TOTAL            13405  23468   6412    12976 12322 617

(如果使用2.3或之前的版本,结果会粗糙一些,很多都被归入了Other,但基本结构是一样的)

stacktrace上有个经常被搜到的帖子对这个格式有说明,虽然针对的是android 2.3格式,但读后非常有收获。
但仍有很多疑问没有解答,例如针对上面的例子,为什么Native heap size那么大,但Pss却那么小?占用内存比较多的Other dev是什么?Unknown又有哪些?等等。
要理解这些,需要知道这个report是如何生成的。实际上,生成report的代码是android的android_os_Debug.cpp
从中我们可以发现,上面列表的数据是由三种方式获取的:
1. Pss/Shared Dirty/Private Dirty三列是读取了/proc/process-id/smaps文件获取的。它会对每个虚拟内存块进行解析,然后生成数据。
2. Native Heap Size/Alloc/Free三列是使用C函数mallinfo得到的。
3. Dalvik Heap Size/Alloc/Free并非该cpp文件产生,而是android的Debug类生成。

后面两个Heap的获取比较简单,我唯一的疑惑是为什么有free的?我的理解是无论是c的malloc还是java的new,最后都是通过mmap系统调用进行内存分配的。而mmap必须以页的4K为单位。所以如果一次一次只需要malloc 2K,则剩下的2K是free的。如果下次再malloc 2K,可以仍然使用上次mmap剩余的2K内存。

至于smaps文件,我们可以通过adb shell cat /proc/process-id/smaps来查看(需要root)。这是个普通的linux文件,描述了进程的虚拟内存区域(vm area)的具体信息。每次mmap一般都会生成一个vm area。
在Android上,一个更加方便的命令是adb shell showmap -a process-id。

第三层 adb shell showmap

该命令也是读取smaps文件,但结果细化的具体的vm area。
该命令输出的每行表示一个vm area,列出了该vm area的start addr, end addr, Vss, Rss, Pss, shared clean, shared dirty, private clean, private dirty,object。 
第二层的dumpsys meminfo其实就是读取这些数据,然后分类(native, dalvik, .so map, etc.)统计生成。
start addr和end addr表示进程空间的起止虚拟地址。
Vss,Rss,Pss跟前面说的一样。
Object可以看做mmap的文件名。

Shared clean,按字面意思,表示共享的干净的数据。共享表示多个进程的虚拟地址可以都指向这块物理空间,表示多个进程共享的so库。为什么这里说是多个进程共享的so而不是所有的so呢?
关于so库的加载,我一直觉得是mmap带MAP_SHARED参数,但看了memory_faq,才知道是MAP_PRIVATE。如果使用showmap命令查看vm area,会发现有的so的内存都属于Shared clean,而有的so则属于private clean。前者一般是当前进程特有的so,而后者一般是通用的so。后来看了对mmap的各种参数的实验(很赞实践精神),才知道第一次以MAP_PRIVATE mmap so,内存都是private clean的。如果另外一个进程mmap了同一个so,那该vm area就变成shared clean了。

Private clean,包括该进程私有的干净的内存。包括前面说的该进程独自使用的so和进程的二进制代码段。
Clean内存的好处是在内存紧张时,可以释放物理内存。因为是clean的,所以不需要写回到disk,只需要下次读取该内存(导致缺页错误)时再从disk读入。

Private dirty,表示该进程私有的不跟disk数据一致的内存段。例如堆(heap),栈(stack),bss段。关于bss段,因为在elf文件为了节约控件没有赋值,所以在加载到内存时赋值为0,于是跟disk就不一致了。在showmap结果中,会发现几乎每个so都有一个显示位[bss]的private dirty段。数据段我估计是private clean的,因为elf文件是有初值的。

Shared dirty开始我一直搞不清楚。后来看了Dalvik vm internal这个video(slides),才明白了些。对于普通的linux进程,当父进程fork子进程时,父进程的虚拟内存区域都会”复制“一份到子进程中。这里”复制“加引号,是因为为了节省内存,也为了减少内存拷贝的时间,使用的是copy-on-write的方法。当子进程对private dirty的堆,栈,bss没有修改时,则是父子进程share这份dirty(因为跟disk没法映射)数据。如果发生改变,则会修改为private dirty。所以android有zygote进程,是所有android apps进程的父进程,在其中会加载resource等资源(下文会看到,最简单的应该也有大概5M resource,例如图片),这些资源都是只读的。具体的apps继承了这些shared dirty的数据,因为不修改它们,所以也不用分配多余的内存空间。

由于android使用的linux没有swap分区,所以dirty的数据必须常驻内存。所以dumpsys meminfo会把private dirty和shared dirty重点列出来,这也是我们优化内存的重点。

现在可以回答一个前面提到的问题,为什么Native Heap(根据mallinfo系统调用得到)很大而Native Pss(根据swaps得到)很小。我觉得这是dumpsys meminfo的一个bug。根据android_os_Debug.cpp的代码,object名字是[heap]的段被认为是native heap。这在2.3是正确的,但在4.0之后,[heap]为名字的段却很小(只有几K)。同时,我却发现有大量的[anon]的区域。我认为anon是anonymous的缩写。malloc一般是通过mmap来分配内存的,而参数是MAP_ANONYMOUS。所以我觉得这些[anon]是native heap。从大小上看,现在这些[anon]被看做是Unkown的一部分,也跟hative heap的大小差不多。

在dumpsys meminfo结果的其他值比较大的行,.so表示映射的so库(vm area行的object名称包含.so字样),.dex表示映射的.dex文件(dalvik的虚拟机二进制码),Other dev表示映射其他的/dev的(dalvik的heap也是映射到特殊的/dev上)。加上native和dalvik的heap,下次写如何具体分析这五项。

本文转载自:http://blog.csdn.net/sfshine/article/details/24288973

共有 人打赏支持
SuShine
粉丝 119
博文 443
码字总数 98932
作品 0
青岛
后端工程师
一步步拆解 LeakCanary

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 java 源码系列 - 带你读懂 Reference 和 ReferenceQueue https://blog.csdn.net/gdutxiaoxu/article/details/80738581 一步步拆解 ...

xujun9411
07/04
0
0
Android性能优化:这是一份详细的布局优化 指南(含、、)

前言 在 开发中,性能优化策略十分重要 本文主要讲解性能优化中的布局优化,希望你们会喜欢。 目录 /** 实例说明:在上述例子,在布局B中 通过标签引用布局C 此时:布局层级为 = RelativeLa...

Carson_Ho
05/14
0
0
Service的生命周期与Activity生命周期区别

碰到一面试题 简述activity/service生命周期? 组件的生命周期 应用程序组件都有一个生命周期,从响应Intent的Android实例开始到这个实例被销毁。在这期间,他们或许有效或许无效,有效时或许...

xiahuawuyu
2012/07/24
0
0
Android性能优化:那些不可忽略的绘制优化

前言 在 开发中,性能优化策略十分重要 本文主要讲解性能优化中的绘制优化,希望你们会喜欢。 目录 // 方式2:在 BaseActivity 的 onCreate() 方法中使用下面的代码移除 优化方案2:移除 控件...

Carson_Ho
05/21
0
0
android面试常见的handler机制 AIDL机制 高级控件UI 内存优化

1,handler机制 在android中的一个异步回调,一个handler允许发送和处理message和runable对象,这些对象和一个线程的MessageQueue相关联。每一个线程实例和一个单独的线程以及线程messageQu...

dominic69
2015/03/20
0
1
性能优化之Java(Android)代码优化

本文主要介绍Java代码中性能优化方式及网络优化,包括缓存、异步、延迟、数据存储、算法、JNI、逻辑等优化方式。 1、降低执行时间 这部分包括:缓存、数据存储优化、算法优化、JNI、逻辑优化...

另类天使
2013/09/30
0
1
内存泄露和LeakCanary的故事

新鲜文章请关注微信公众号: JueCode 今天我们来聊一聊Android中的内存泄露和内存泄露的检测工具LeakCanary。Java有垃圾回收线程为什么还会发生内存泄露?原因就是外部人为持有对象引用,持有...

juexingzhe
05/22
0
0
Android开发性能优化总结

Android性能调优包含 移动网络优化 Java(Android)代码优化 布局优化 数据库性能优化 参考链接 http://www.trinea.cn/android/android-traceview/ 本文主要针对代码调优 应用程序的性能问题体...

蜗牛崛起
2017/10/19
0
0
Android性能优化:手把手带你全面实现内存优化

前言 在 Android开发中,性能优化策略十分重要 本文主要讲解性能优化中的内存优化,希望你们会喜欢 目录 1. 定义 优化处理 应用程序的内存使用、空间占用 2. 作用 避免因不正确使用内存 & 缺...

codeGoogle
05/08
0
0
android activity详解二:Activity的生命周期

管理Activity的生命周期 通过实现activity的生命周期回调方法来管理你的activity,是创造既稳定又灵活的activity的关键。Activity的生命周期直接受到相关的其它activity和它的任务以及所在栈...

晨曦之光
2012/03/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

七、JSP九大内置对象和四个作用域

九大内置对象: request:类型是HttpServletRequest,和Servlet里的HttpServletRequest一模一样。 response:类型是HttpServletResponse,和Servlet里的HttpServletResponse一模一样。JSP里基...

Wakeeee_
10分钟前
0
0
Confluence 6 配置快捷链接

快捷链接提供了提供了一个在 Confluence 中访问从常用资源的快速方法。当你创建一个快捷链接的时候,你需要为你的快捷链接 URL 指派一个 Key,用户可以在 Confluence 中只输入这个 Key 用来替...

honeymose
11分钟前
0
0
我的第一篇博文

网络界的前辈们好。本人从接触网络到你现在也有4、5年的时间了,期间不断的通过网络学习,当然也没少看大牛们给的建议。 2011年的9月份,如愿以偿的上了“大学”,刚上大学就接触到了一门叫做...

yeahlife
36分钟前
0
0
第十四章NFS服务搭建与配置

14.1 NFS介绍 NFS介绍 NFS是Network File System的缩写;这个文件系统是基于网路层面,通过网络层面实现数据同步 NFS最早由Sun公司开发,分2,3,4三个版本,2和3由Sun起草开发,4.0开始Netap...

Linux学习笔记
42分钟前
0
0
双向认证-nginx

1、设置容器 docker run -it --name nginx-test2 -v /home/nginx:/apps -v /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro -p 8183:80 -p 7443:443 -d nginx:stable 2、修改nginx配......

hotsmile
43分钟前
0
0
深入了解 Java 自动内存管理机制及性能优化

一图带你看完本文 一、运行时数据区域 首先来看看Java虚拟机所管理的内存包括哪些区域,就像我们要了解一个房子,我们得先知道这个房子大体构造。根据《Java虚拟机规范(Java SE 7 版)》的规...

Java大蜗牛
45分钟前
4
0
SpringBoot | 第六章:常用注解介绍及简单使用

前言 之前几个章节,大部分都是算介绍springboot的一些外围配置,比如日志 配置等。这章节开始,开始总结一些关于springboot的综合开发的知识点。由于SpringBoot本身是基于Spring和SpringMvc...

oKong
45分钟前
9
0
云数据库架构演进与实践

如今,大型企业如金融企业和银行等,在下一代的微服务架构转型要求下,需要基础软件和数据平台能够实现原生的云化,以满足微服务架构的需求。 微服务,也就是一种面向服务的,有特定边界的松...

巨杉数据库
47分钟前
0
0
Linux系统梳理---系统搭建(一):jdk卸载与安装

1.去官网下载符合Linux版本的jdk,暂用jdk-8u171-linux-x64.rpm 2.登陆Linux,进入usr目录,创建java目录(方便管理,可以其他位置):mkdir java 3.上传下载的jdk包至Linux服务器,使用rz指令(sz f...

勤奋的蚂蚁
57分钟前
0
0
Linux Kernel 4.16 系列停止维护,用户应升级至 4.17

知名 Linux 内核维护人员兼开发人员 Greg Kroah-Hartman 近日在发布 4.16.18 版本的同时,宣布这是 4.16 系列的最后一个维护版本,强烈建议用户立即升级至 4.17 系列。 Linux 4.16 于 2018 年...

六库科技
59分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部