一次内存泄漏问题的发现与解决过程
来源:互联网 发布:高丝 艾文莉 知乎 编辑:程序博客网 时间:2024/05/17 23:27
一次内存泄漏问题的发现与解决过程
新手第一次解决内存泄漏问题,把过程分享一下。大神飘过就好。。。
0x00 发现问题
这个内存泄漏的问题是在解决另一个bug的过程中发现的,在发起报销的过程中,当一个报销类别的表单中包含了图片元素时,如果添加了图片,然后重复编辑几次,app会变的非常卡顿,最初怀疑是图片加载时没有压缩直接加载了原图,于是开始关注android studio的内存监控。
添加八张图片,重复编辑了若干次以后,内存占用如图所示:
很显然,内存有一个明显升高的过程,怀疑产生了内存泄漏。
0x01 分析问题
由于审批模块涉及的类很多,一时间也无法找到究竟是在哪里发生了问题,于是想到了使用MAT(Memory Analyzer tool)来分析,那么首先要把发生泄漏的内存dump出来,打开ddms,开始重复刚刚产生内存泄漏的过程。
这一次我添加了4张图片到表单中,以下是每一次编辑审批单结束后,gc一次之后的数据:
填写完保存后:
第一次编辑:
第二次编辑:
第三次编辑:
我们主要关注Heap Size和Allocated的值,Heap Size是虚拟机分配的堆大小,这个值是会动态改变的,当堆的空间不够时,这个值会增大,当堆的值过大,堆的利用率降低时,这个值会变小,Allocated是当前堆空间中已经分配的大小,一次gc之后,资源被回收,这个值应该会变小,但是可以看到,每一次gc之后,Allocated的值都会增大25m左右,这说明有高达25m的内存没有被回收。那么具体是哪些资源没有被回收呢?需要借助mat工具来分析。
0x02 进一步分析
首先将内存dump出来,并使用sdk中的hropf-conv
工具转换一下,用mat打开,在Leak Suspects视图中,可以看到当前内存的分配情况,如图所示:
mat的分析结果显示,有4个地方可能产生了内存泄漏,分别是:
前三个是那个自定义的可删除列表控件,最后一个是imageloader,最后那个我猜想应该是imageloader的内存缓存,估计是误报,那么前三个就应该是这次内存泄漏的元凶了。
DeleteableListView是用来展示图片的,如果这个控件没有在gc的时候销毁,那么它持有的图片资源也自然不会释放,众所周知,图片是非常占用内存的,但是,为什么DeleteableListView没有被销毁呢,当展示表单的activity被finish时,这个activity持有的视图资源应该会被gc掉的,如果没有被gc,那说明一定还有其他类持有了这个DeleteableListView的实例。
这个时候,就需要来仔细检查这个DeleteableListView实例的引用关系了,使用mat,在这个实例上右键然后Path to GC Roots
,我们可以看到引用关系,如图所示
这个实例应该是被两个类持有,一个是BaseForm,另外一个间接被EventBus持有,被BaseForm持有很好理解,因为Baseform继承了Linearlayout,实际上就是DeleteableListView的父布局,但是为什么会间接被EventBus持有呢?仔细看一下引用关系不难发现,其实,这是因为DeleteableListView被ImageElement引用,ImageElement又被EventBus引用。这样,问题就很清楚了。
0x03 解决问题
在ImageElement中,我将这个类注册到了eventbus,用来处理一系列事件,比如删除,添加等,为了将逻辑和视图解耦,ImageElement并不是一个继承自View的类,而是将视图包装在了自身内部,通过bind()方法返回给外层的视图容器,这样一来,我需要将ImageElement的生命周期和view的生命周期绑定在一起,没错,我确实这么做了,而且在imageelement的onDetachedFromWindow方法中我也将这个类从eventbus中移除了,但是,在Activity的onDestory方法中,我忘记调用BaseForm的onDestory方法来触发每一个元素的onDetachedFromWindow方法。
好吧,真想给自己一巴掌。。
补充上了onDestory后,再次尝试一下添加图片后多次重新编辑,内存占用如下:
问题被解决了!
0x04 总结
总结了一下这次问题产生的原因,最重要的有两点,第一是审批部分的代码写的太过复杂,有过度设计的嫌疑,导致了根本无法直观的发现问题所在,另一点,就是对eventbus的理解不够准确,以前在activity中直接使用时,只是按照习惯在oncreate和ondestory中调用一下注册和解绑的方法,没有理解这样做的原因。看来以后使用第三方库时,还是应该多看文档,有空还是需要继续RTFSC。
- 一次内存泄漏问题的发现与解决过程
- 一次GTK程序内存泄露的解决过程发现的两个内存泄露的问题
- 一次内存泄漏问题定位过程与分析
- 一次调试内存泄漏的过程
- 记录一次Android内存泄漏事件和解决过程
- 记一次通过Memory Analyzer分析内存泄漏的解决过程
- 浏览器内存泄漏问题的跟踪与解决
- 浏览器内存泄漏问题的跟踪与解决--CSDN[cutesource]
- 浏览器内存泄漏问题的跟踪与解决
- 关于Android handler内存泄漏问题的测试与解决
- InputMethodManager引起的内存泄漏问题分析与解决
- 使用JRockit作为工具检测并解决JAVA内存泄漏问题的一次实战
- 使用JRockit作为工具检测并解决JAVA内存泄漏问题的一次实战.doc
- 【转】使用JRockit作为工具检测并解决JAVA内存泄漏问题的一次实战
- 一次使用vld检测内存泄漏的修正过程
- 记录一次kernel内存泄漏的查找定位过程
- 一次内存泄漏导致的OOM实例分析和解决
- 解决内存泄漏问题
- 利用ssh传输文件
- android studio总结
- svn 中如何checkout出单个文件
- LeetCode---ZigZag Conversion
- NBUT 1624 死胡同(可以用搜索,也可以不用)
- 一次内存泄漏问题的发现与解决过程
- dubbo extension扩展点 源代码
- ECharts 实时折线
- ab测试
- IOS 关于Text Field设置键盘
- Zookeeper的事件驱动和订阅模式
- Android中常见的热门标签的流式布局的实现
- 从哪里找寻安全感?
- HDU 2255 奔小康赚大钱 KM模板题