一:内存分析
除了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"
);
//
修改这里
*
}
![](https://img-blog.csdnimg.cn/de9c720028d841ecb689bd721dff51da.png)
4. 内存指标
5. 常用的内存调优分析命令:
1. dumpsys meminfo
2. procrank
3. cat /proc/meminfo
4. free
5. showmap
6. vmstat
dumpsys meminfo
![](https://img-blog.csdnimg.cn/77b34746223c4b5db24c7b5be36bce51.png)
相关参数说明:
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
![](https://img-blog.csdnimg.cn/f5da5d4983e9473a9a0004bf110041d9.png)
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
![](https://img-blog.csdnimg.cn/e04de3145fca41babec4444129e419b5.png)
![](https://img-blog.csdnimg.cn/212fd71502d04e2fb3539a0443ec9765.png)
三. 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
可以回收,否则还是会内存泄漏。
6、Handler临时性内存泄漏
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
,再退出程序
8、WebView
WebView
都存在内存泄漏的问题,在应用中只要使用一次
WebView
,内存就不会被释放掉。我们可以为WebView开启一个独立的进程,使用
AIDL
与应用的主进程进行通信,
WebView
所在的进程可以根据业 务的需要选择合适的时机进行销毁,达到正常释放内存的目的。
9、使用ListView时造成的内存泄漏
在构造
Adapter
时,使用缓存的
convertView
。