android中hprof文件分析

2024-09-04 01:48
文章标签 分析 android hprof

本文主要是介绍android中hprof文件分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Hprof基本概念

hprof最初是由J2SE支持的一种二进制堆转储格式,hprof文件保存了当前java堆上所有的内存使用信息,能够完整的反映虚拟机当前的内存状态。
格式
在这里插入图片描述
Hprof文件由FixedHead和一系列的Record组成,Record包含字符串信息、类信息、栈信息、GcRoot信息、对象信息。每个Record都是由1个字节的Tag、4个字节的Time、4个字节的Length和Body组成,Tag表示该Record的类型,Body部分为该Record的内容,长度为Length。

Android中的hprof

在Android设备上,我们可以通过两种方式生成当前进程的hprof文件

通过调用Debug.dumpHprofData(String filePath)方法来生成hprof文件
通过执行shell命令adb shell am dumpheap pid /data/local/tmp/x.hprof来生成指定进程的hprof文件到目标目录

然而Android平台上的hprof文件和标准Java的hprof定义有一些区别,主要是版本号不一样,而且增加了一些特殊Tag,在art/runtime/hprof/hprof.cc中有如下定义
在这里插入图片描述
可以看出Android增加了额外的9个HeapTag,其中比较重要的是HPROF_HEAP_DUMP_INFO。Android上将java堆分为Heap-App、Heap-Image、Heap-Zygote三块,这个Tag的作用是切换当前的堆,该Tag后面紧跟着一个4个字节的堆id和一个堆名称id。
比如出现一个HPROF_HEAP_DUMP_INFO Record,且该Record表示Heap-Image,那么表示后续所有Record中的类、对象、GCRoot对象均在Heap-Image中存储,直到下一个HPROF_HEAP_DUMP_INFO出现为止。

内存泄漏

内存泄漏指的是一个本该被回收的对象因为某些原因导致其不能被回收,通俗来说就是该对象理论上不再使用,但是仍无法被回收。

Android中的泄漏对象

判断一个对象是否泄漏首先要判断该对象是否不再使用,想要判断这一点则需要对象有明显的生命周期,在Android中有以下对象可以判断是否泄漏:

1、Activity: 通过Activity的mDestroyed属性来判断该Activity是否已经销毁,如果已经销毁且未被回收则认为是泄漏
Fragment: 通过Fragment的mFragmentManager是否为空来判断该Fragment是否处于无用状态,如果mFragmentManager为空且未被回收则认为是泄漏。

2、View: 通过unwrapper mContext获得Activity,如果存在Activity,则判断该Activity是否泄漏。

Editor: Editor指的是android.widget包下的Editor,是用于TextView处理editable text的辅助类,通过mTextView是否为空来判断Editor是否处于无用状态,如果mTextView为空且未被回收则认为是泄漏。

3、ContextWrapper: 通过unwrapper ContextWrapper获得Activity,如果存在Activity,则判断该Activity是否泄漏。

4、Dialog: 通过mDecor是否为空判断该Dialog是否处于无用状态,如果mDecor为空且未被回收则认为是泄漏。

5、MessageQueue: 通过mQuitting或者mQuiting(应该是历史原因,前期拼写错误为mQuiting,后来改正)来判断MessageQueue是否已经退出,如果已经退出且未被回收则认为是泄漏。

ViewRootImpl: 通过ViewRootImpl的mView是否为空来判断该ViewRootImpl是否处于无用状态,如果mView为空且未被回收则认为是泄漏。

6、Window: 通过mDestroyed来判断该Window是否处于无用状态,如果mDestroyed为true且未被回收则认为是泄漏。
Toast: 拿到mTN,通过mTN的mView是否为空来判断当前Toast是否已经hide,如果已经hide且未被回收则认为是泄漏。

泄漏的形式

泄漏的本质就是无用对象被持有导致无法回收,具体的形式有如下几种:

1、非静态内部类、匿名内部类持有外部类对象引用: 一般为用于回调的Listener,该Listener被别的地方持有,间接导致外部类对象被泄漏。
2、Handler: 在Activity中定义Handler对象的时候,Handler持有Activity同时Message持有Handler,而Message被MessageQueue持有,最终导致Activity泄漏。

3、资源对象未关闭: 数据库连接、Cursor、IO流等使用完后未close。

4、属性动画: 使用ValueAnimator和ObjectAnimator的时候,未及时关闭动画导致泄漏。Animator内部向AnimationHandler注册listener,AnimationHandler是一个单例,如果不及时cancel,会导致Animator泄漏,间接导致Activity/Fragment/View泄漏(比如Animator的updateListener一般都以匿名内部类实现)

5、逻辑问题: 注册监听器之后未及时解注册,比如使用EventBus的时候没有在合适的时候进行解注册

profiler使用

通过下载hprof文件,解压缩后,直接拖到Android studio中,可以通过Android studio中的profiler工具进行分析,如下图:
1、Heap Dump
1、筛选
左边区域可以通过筛选查看到泄漏的activity或者fragment,在Android中的泄漏主要是activity或者fragment有生命周期的类泄漏。
2、Heap区分
Heap 中主要分为三种:
在这里插入图片描述
1、app heap
当前APP从堆中分配的内存
2、image heap
系统启动映像,包含启动期间预加载的类。 此处的分配保证绝不会移动或消失
3、zygote heap
zygote是所有APP进程的母进程,linux 的进程使用COW技术,所有的APP共享zygote的内存空间,因此堆的话也继承了,并且zygote的这些空间不允许写入,为了加快java的启动和运行速度,zygote在启动时预加载了许多资源和代码,这样可以提高APP的运行速率.
源码

enum HprofHeapId {HPROF_HEAP_DEFAULT= 0,HPROF_HEAP_ZYGOTE= 'Z',HPROF_HEAP_APP= 'A',HPROF_HEAP_IMAGE= 'I',
};if (space->IsZygoteSpace()) {heap_type= HPROF_HEAP_ZYGOTE;VisitRoot(obj, RootInfo(kRootVMInternal));} else if (space->IsImageSpace() && heap->ObjectIsInBootImageSpace(obj)) {// Only countobjects in the boot image as HPROF_HEAP_IMAGE, this leaves app image objects as// HPROF_HEAP_APP.b/35762934heap_type= HPROF_HEAP_IMAGE;VisitRoot(obj, RootInfo(kRootVMInternal));}} else {const auto* los = heap->GetLargeObjectsSpace();if (los->Contains(obj) && los->IsZygoteLargeObject(Thread::Current(), obj)) {heap_type= HPROF_HEAP_ZYGOTE;VisitRoot(obj, RootInfo(kRootVMInternal));}

2、Instance View

点击heap dump中具体的类,右边就会展示instance view
Instance view中主要展示对象相关的信息
在这里插入图片描述

1、Depth
depth是从gc roots到选中的当前对象的引用链最短长度。在java垃圾回收机制中,被gc roots引用到的对象不会被回收。如下图:在gc root 引用树之外的对象会被回收。
在这里插入图片描述
Android中gc root 主要分为下面几类:
在这里插入图片描述
2、Native Size
native对象的内存大小,这个值只有在Android 7.0以及更高的版本可见。
3、Shallow Size
对象本身占用的内存,不包括它引用的其他实例

Shallow Size = [类定义] + 父类fields所占空间 + 自身fields所占空间 + [alignment]

4、Retained Size
Retained Size是指, 当实例A被回收时, 可以同时被回收的实例的Shallow Size之和,所以进行内存分析时,应该重点关注Retained Size较大的实例; 或者可以通过Retained Size判断出某A实例内部使用的实例是否被其他实例引用.
如下图:
在这里插入图片描述
上图中:obj1的retained size=obj1+obj2+obj4的shallow size,注意由于obj3被gc roots直接引用,所以这个值不计算到obj1的retained size中。
在这里插入图片描述
如上图:obj1的retained size=obj1+obj2+ obj3+obj4的shallow size

3、Reference

reference里面主要是类的引用关系,可以通过引用关系,一层一层的查看类是如何泄漏的。

参考

1、Android中内存泄漏相关
2、hprof文件相关
3、profile分析内存泄漏

这篇关于android中hprof文件分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1134655

相关文章

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.