性能优化----内存分析

一:内存分析

除了Profiler 抓取出来的文件,还可以用安卓api 抓取,如下

将dump 出来的文件,转一下,转成MAT工具可以识别的。

转换前,需要配置环境变量

转换的命令和操作

下载MAT 工具

Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation

下载后,首次打开的界面长成如下的样子:

 

二. 如何查看Android设备对App的内存限制

1. 通过指令 adb shell cat /system/build.prop

2. 通过代码的api

ActivityManager activityManager =
( ActivityManager ) context . getSystemService ( Context . ACTIVITY_SERVICE )
activityManager . getMemoryClass (); // m 为单位

 

3.可以修改Android设备对App的内存限制吗?

int AndroidRuntime :: startVm ( JavaVM ** pJavaVM , JNIEnv ** pEnv , bool zygote )
{
/*
* The default starting and maximum size of the heap. Larger
* values should be specified in a product property override.
*/
parseRuntimeOption ( "dalvik.vm.heapstartsize" , heapstartsizeOptsBuf ,
"-Xms" , "4m" );
parseRuntimeOption ( "dalvik.vm.heapsize" , heapsizeOptsBuf , "-Xmx" ,
"16m" ); // 修改这里
* }

4. 内存指标

 

 5. 常用的内存调优分析命令:

1. dumpsys meminfo
2. procrank
3. cat /proc/meminfo
4. free
5. showmap
6. vmstat

dumpsys meminfo

 

 相关参数说明:

Pss Total :是一个进程实际使用的内存,该统计方法包括比例分配共享库占用的内存,即如果有三个进
程共享了一个共享库,则平摊分配该共享库占用的内存。 Pss Total 统计方法的一个需要注意的地方是如
果使用共享库的一个进程被杀死,则共享库的内存占用按比例分配到其他共享该库的进程中,而不是将
内存资源返回给系统,这种情况下 PssTotal 不能够准确代表内存返回给系统的情况。
Private Dirty :进程私有的脏页内存大小,该统计方法只包括进程私有的被修改的内存。
Private Clear :进程私有的干净页内存大小,该统计方法只包括进程私有的没有被修改的内存。
Swapped Dirty :被交换的脏页内存大小,该内存与其他进程共享。
其中 private Dirty + private Clean = Uss ,该值是一个进程的使用的私有内存大小,即这些内存唯一被
该进程所有。该统计方法真正描述了运行一个进程需要的内存和杀死一个进程释放的内存情况,是怀疑
内存泄露最好的统计方法。
共享比例: sharing_proportion = (Pss Total - private_clean - private_dirty) / (shared_clean +
shared_dirty)
享学课堂 能够被共享的内存: swappable_pss = (sharing_proportion * shared_clean) + private_clean
Native Heap :本地堆使用的内存,包括 C/C++ 在堆上分配的内存
Dalvik Heap dalvik 虚拟机使用的内存
Dalvik other :除 Dalvik Native 之外分配的内存,包括 C/C++ 分配的非堆内存
Cursor :数据库游标文件占用的内存
Ashmem :匿名共享内存
Stack Dalvik 栈占用的内存
Other dev :其他的 dev 占用的内存
.so mmap so 库占用的内存
.jar mmap .jar 文件占用的内存
.apk mmap .apk 文件占用的内存
.ttf mmap .ttf 文件占用的内存
.dex mmap .dex 文件占用的内存
image mmap :图像文件占用的内存
code mmap :代码文件占用的内存
Other mmap :其他文件占用的内存
Graphics GPU 使用图像时使用的内存
GL GPU 使用 GL 绘制时使用的内存
Memtrack GPU 使用多媒体、照相机时使用的内存
Unknown :不知道的内存消耗
Heap Size :堆的总内存大小
Heap Alloc :堆分配的内存大小
Heap Free :堆待分配的内存大小
Native Heap | Heap Size : mallinfo usmblks 获的,当前进程 Native 堆的最大总共分配内存
Native Heap | Heap Alloc : mallinfo uorblks 获的,当前进程 navtive 堆的总共分配内存
Native Heap | Heap Free : mallinfo fordblks 获的,当前进程 Native 堆的剩余内存
Native Heap Size Native Heap Alloc + Native Heap Free
mallinfo 是一个 C 库, mallinfo() 函数提供了各种各样通过 malloc() 函数分配的内存的统计信息。
Dalvik Heap | Heap Size : Runtime totalMemory() 获得, Dalvik Heap 总共的内存大小
Dalvik Heap | Heap Alloc : Runtime totalMemory() - freeMemory() 获得, Dalvik Heap 分配的内存
大小
Dalvik Heap | Heap Free : Runtime freeMemory() 获得, Dalvik Heap 剩余的内存大小
Dalvik Heap Size = Dalvik Heap Alloc + Dalvik Heap Free
Obejcts 当前进程中的对象个数
Views: 当前进程中实例化的视图 View 对象数量
ViewRootImpl: 当前进程中实例化的视图根 ViewRootImpl 对象数量
AppContexts: 当前进程中实例化的应用上下文 ContextImpl 对象数量
Activities: 当前进程中实例化的 Activity 对象数量
Assets: 当前进程的全局资产数量
AssetManagers: 当前进程的全局资产管理数量
Local Binders: 当前进程有效的本地 binder 对象数量
Proxy Binders: 当前进程中引用的远程 binder 对象数量
Death Recipients: 当前进程到 binder 的无效链接数量
OpenSSL Sockets: 安全套接字对象数量
SQL
MEMORY_USED: 当前进程中数据库使用的内存数量, kb
PAGECACHE_OVERFLOW: 页面缓存的配置不能够满足的数量, kb
MALLOC_SIZE: sqlite3 请求的最大内存分配数量, kb
DATABASES
pgsz: 数据库的页面大小
dbsz: 数据库大小
Lookaside(b): 后备使用的内存大小
cache: 数据缓存状态
Dbname: 数据库表名
Asset Allocations 资源路径和资源大小

procrank

 

 

 cat /proc/meminfo

 输出结果如下(结果内存值不带小数点,此处添加小数点的目的是为了便于比对大小)

root@phone:/ # cat /proc/meminfo
MemTotal:  2857.032 kB //RAM 可用的总大小 ( 即物理总内存减去系统预留和内核二进 制代码大小)
MemFree:       1020.708 kB //RAM 未使用的大小
Buffers:            75.104 kB // 用于文件缓冲
Cached:           448.244 kB // 用于高速缓存
SwapCached: 0 kB // 用于 swap 缓存
Active:             832.900 kB // 活跃使用状态,记录最近使用过的内存,通常不回收用于其 它目的
Inactive: 391.128 kB // 非活跃使用状态,记录最近并没有使用过的内存,能够被回收用于其他目的
Active(anon):     700.744 kB //Active = Active(anon) + Active(file)
Inactive(anon): 228 kB //Inactive = Inactive(anon) + Inactive(file)
Active(file):       132.156 kB
Inactive(file):     390.900 kB

 

showmap

主功能:用于查看虚拟地址区域的内存情况

用法: showmap -a  进程id

 

vmstat

 

 

 

三. Android内存泄漏常见场景以及解决方案

1、资源性对象未关闭

对于资源性对象不再使用时,应该立即调用它的 close() 函数,将其关闭,然后再置为 null 。例如 Bitmap等资源未关闭会造成内存泄漏,此时我们应该在Activity 销毁时及时关闭。

2、注册对象未注销

例如 BraodcastReceiver EventBus 未注销造成的内存泄漏,我们应该在 Activity 销毁时及时注销。

3、类的静态变量持有大数据对象

尽量避免使用静态变量存储数据,特别是大数据对象,建议使用数据库存储。

4、单例造成的内存泄漏

优先使用 Application Context ,如需使用 Activity Context ,可以在传入 Context 时使用弱引用进行封装,然后,在使用到的地方从弱引用中获取Context ,如果获取不到,则直接 return 即可。

5、非静态内部类的静态实例

该实例的生命周期和应用一样长,这就导致该静态实例一直持有该 Activity 的引用, Activity 的内存资源不能正常回收。此时,我们可以将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context ,尽量使用 Application Context ,如果需要使用 Activity Context ,就记得用完后置空让GC 可以回收,否则还是会内存泄漏。

6Handler临时性内存泄漏

Message 发出之后存储在 MessageQueue 中,在 Message 中存在一个 target ,它是 Handler 的一个引用,Message Queue 中存在的时间过长,就会导致 Handler 无法被回收。如果 Handler 是非静态的,则会导致Activity 或者 Service 不会被回收。并且消息队列是在一个 Looper 线程中不断地轮询处理消息,当这个Activity 退出时,消息队列中还有未处理的消息或者正在处理的消息,并且消息队列中的 Message持有Handler 实例的引用, Handler 又持有 Activity 的引用,所以导致该 Activity 的内存资源无法及时回 收,引发内存泄漏。解决方案如下所示:
1 、使用一个静态 Handler 内部类,然后对 Handler 持有的对象(一般是 Activity )使用弱引用,这
样在回收时,也可以回收 Handler 持有的对象。
2、在 Activity Destroy 或者 Stop 时,应该移除消息队列中的消息,避免 Looper 线程的消息队列中有待处理的消息需要处理。
需要注意的是,AsyncTask 内部也是 Handler 机制,同样存在内存泄漏风险,但其一般是临时性的。对于类似AsyncTask 或是线程造成的内存泄漏,我们也可以将 AsyncTask Runnable 类独立出来或者使用静 态内部类。

7、容器中的对象没清理造成的内存泄漏

在退出程序之前,将集合里的东西 clear ,然后置为 null ,再退出程序

8WebView

WebView 都存在内存泄漏的问题,在应用中只要使用一次 WebView ,内存就不会被释放掉。我们可以为WebView开启一个独立的进程,使用 AIDL 与应用的主进程进行通信, WebView 所在的进程可以根据业 务的需要选择合适的时机进行销毁,达到正常释放内存的目的。

9、使用ListView时造成的内存泄漏

在构造 Adapter 时,使用缓存的 convertView

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值