Android 应用程序内存泄漏的分析
- 格式:docx
- 大小:851.38 KB
- 文档页数:6
Android内存泄漏终极解决方法介绍Android内存泄漏终极解决方法介绍一、概述在Android内存泄漏终极解决篇(上)中我们介绍了如何检查一个App是否存在内存泄漏的问题,本篇将总结典型的内存泄漏的代码,并给出对应的解决方案。
内存泄漏的主要问题可以分为以下几种类型:静态变量引起的内存泄漏非静态内部类引起的内存泄漏资源未关闭引起的内存泄漏二、静态变量引起的内存泄漏在java中静态变量的生命周期是在类加载时开始,类卸载时结束。
换句话说,在android中其生命周期是在进程启动时开始,进程死亡时结束。
所以在程序的运行期间,如果进程没有被杀死,静态变量就会一直存在,不会被回收掉。
如果静态变量强引用了某个Activity中变量,那么这个Activity就同样也不会被释放,即便是该Activity执行了onDestroy(不要将执行onDestroy和被回收划等号)。
这类问题的解决方案为:1.寻找与该静态变量生命周期差不多的替代对象。
2.若找不到,将强引用方式改成弱引用。
比较典型的例子如下:单例引起的Context内存泄漏public class IMManager { private Context context; private static IMManager mInstance; public static IMManager getInstance(Context context) { if (mInstance == null) { synchronized (IMManager.class) { if (mInstance == null) mInstance = new IMManager(context); } } return mInstance; } private IMManager(Context context) { this.context = context; }} 当调用getInstance时,如果传入的context是Activity的'context。
详解Android内存泄露及优化⽅案⽬录⼀、常见的内存泄露应⽤场景?1、单例的不恰当使⽤2、静态变量导致内存泄露3、⾮静态内部类导致内存泄露4、未取消注册或回调导致内存泄露5、定时器Timer 和 TimerTask 导致内存泄露6、集合中的对象未清理造成内存泄露7、资源未关闭或释放导致内存泄露8、动画造成内存泄露9、WebView 造成内存泄露总结⼀、常见的内存泄露应⽤场景?1、单例的不恰当使⽤单例是我们开发中最常见和使⽤最频繁的设计模式之⼀,所以如果使⽤不当就会导致内存泄露。
因为单例的静态特性使得它的⽣命周期同应⽤的⽣命周期⼀样长,如果⼀个对象已经没有⽤处了,但是单例还持有它的引⽤,那么在整个应⽤程序的⽣命周期这个对象都不能正常被回收,从⽽导致内存泄露。
如:public class App {private static App sInstance;private Context mContext;private App(Context context) {this.mContext = context;}public static App getInstance(Context context) {if (sInstance == null) {sInstance = new App(context);}return sInstance;}}调⽤getInstance(Context context)⽅法时传⼊的上下⽂如果为当前活动Activity或者当前服务的Service以及当前fragment的上下⽂,当他们销毁时,这个静态单例sIntance还会持⽤他们的引⽤,从⽽导致当前活动、服务、fragment等对象不能被回收释放,从⽽导致内存泄漏。
这种上下⽂的使⽤很多时候处理不当就会导致内存泄漏,需要我们多注意编码规范。
2、静态变量导致内存泄露静态变量存储在⽅法区,它的⽣命周期从类加载开始,到整个进程结束。
AndroidHandler内存泄漏详解及其解决⽅案关联篇:关联篇:在android开发过程中,我们可能会遇到过令⼈奔溃的OOM异常,⾯对这样的异常我们是既熟悉⼜深恶痛绝的,因为造成OOM 的原因有很多种情况,如加载图⽚过⼤,某已不再使⽤的类未被GC及时回收等等......本篇我们就来分析其中⼀种造成OOM的场景,它就是罪恶的内存泄漏。
对于这样的称呼,我们并不陌⽣,甚⾄屡次与之"并肩作战",只不过它就是⼀个猪队友,只会不断送塔.......本篇分为3部分:1.Handler内存泄漏例⼦说明以及原理阐明2.问题验证(如果感觉繁琐请直接跳过)3.Handler内存泄漏解决⽅法1.Handler内存泄漏例⼦说明以及原理阐明Handler,我们已经相当熟悉了,⽽且经常⽤得不亦乐乎,但就是因为太熟悉了,才会偶尔被它反捅⼀⼑,⾎流不⽌......还记得我们曾经满怀信⼼地使⽤着如下的优美⽽⼜简洁的代码不?不怕你吓着,实话告诉你,这个代码已经造成内存泄漏了不相信?我们使⽤Android lint⼯具检测⼀下该类的代码:⾯对现实吧,那为什么会这样呢?在java中⾮静态内部类和匿名内部类都会隐式持有当前类的外部引⽤,由于Handler是⾮静态内部类所以其持有当前Activity的隐式引⽤,如果Handler没有被释放,其所持有的外部引⽤也就是Activity也不可能被释放,当⼀个对象⼀句不需要再使⽤了,本来该被回收时,⽽有另外⼀个正在使⽤的对象持有它的引⽤从⽽导致它不能被回收,这导致本该被回收的对象不能被回收⽽停留在堆内存中,这就产⽣了内存泄漏(上⾯的例⼦就是这个原因)。
最终也就造成了OOM.......我们再来段清晰的代码,我们来使⽤mHandler发送⼀个延迟消息:分析:当我们执⾏了HandlerActivity的界⾯时,被延迟的消息会在被处理之前存在于主线程消息队列中5分钟,⽽这个消息中⼜包含了Handler的引⽤,⽽我们创建的Handler⼜是⼀个匿名内部类的实例,其持有外部HandlerActivity的引⽤,这将导致了HandlerActivity⽆法回收,进⾏导致HandlerActivity持有的很多资源都⽆法回收,从⽽就造成了传说中的内存泄露问题!2.问题验证(如果感觉繁琐请直接跳过)为了进⼀步验证内存泄漏问题,我们在该类中创建⼀个int数组,该数组分配的内存⼤⼩为2m,然后我们⽤DDMS来查看heap内存,然后使⽤GC回收,看看内存会不会有变化:第⼀次启动app时,head内存为12.5M,已经分配内容(Allocated):8.5M,空闲内存:4M,我们频繁点击GC按钮,内存并没有发⽣明显变化,现在我们点击⼿机返回健,推出应⽤,然后再重新进⼊,同样检测⼀下head内存:我们发现head内存:20.5M,Allocated:16.5M,Free:4M,堆内存和已经分配内存近乎翻倍,我们继续频繁点击GC, 看看能否被回收?结果内存并没有明显变化,现在我们继续点击⼿机返回健,推出应⽤,然后再重新进⼊,同样再次检测⼀下head内存:我们发现head内存:28.5M,Allocated:24.5M,Free:4M,堆内存和已经分配内存⼜增加了,⽽且⽆论我们如何点击GC回收内存,内存都没有明显变化,⽽且每启动⼀次该页⾯,内存就增加⼀倍!这也就说存在在某个类只创建⽽没销毁的情况,其实就是存在内存泄漏问题。
移动应用中的内存泄漏分析在现代社会中,移动应用成为人们生活中不可或缺的一部分。
开发一个高品质的移动应用需要开发人员关注各个方面的问题,其中,内存泄漏是其中之一。
内存泄漏是移动应用中经常遇到的问题,它会导致应用程序内存占用过多,使应用程序运行时变得缓慢或崩溃。
本文将介绍移动应用中的内存泄漏问题,并提供了解决内存泄漏难题的方法。
什么是内存泄漏?内存泄漏是指应用程序在使用内存时没有正确地管理内存的释放。
当应用程序需要分配动态内存时,操作系统会在堆中分配一块内存。
当应用程序使用完该内存时,应该通过释放函数来释放这块内存以便应用程序后续的内存使用。
当应用程序没有正确释放该内存时,这块内存就被占用,这就是内存泄漏。
内存泄漏的原因在移动应用中,内存泄漏的原因可能有很多,有些是由于开发人员不小心编写的代码造成的,而有些则是由于操作系统引起的。
以下是一些可能导致内存泄漏的原因:1. 对象生命周期问题创建的对象一定要在使用完之后释放。
如果对象没有被正确的释放,那么这个对象所占用的内存就会被浪费掉,从而导致内存泄漏。
内存泄漏主要是由于程序没有删除已经创建的对象或没有正确使用指针来引用这些对象。
2. 指针操作指针是一个指向另外一个内存区域的变量。
对于指针而言,它必须指向合适的内存区域。
如果程序没有正确使用指针来引用对象,那么可能会发生内存泄漏。
解决内存泄漏问题的方法以下是几种解决内存泄漏问题的方法:1. 使用工具检测工具可以检测应用程序中的内存泄漏。
有许多内存泄漏检测工具可供选择,例如,MallocDebug、MemoryChecker等工具。
使用这些工具可以帮助开发人员快速和准确地识别并处理内存泄漏的问题。
2. 使用显式释放函数显式释放函数在使用完一个对象时,会自动将其从内存中删除。
显式释放函数还可以确保内存释放的准确性,从而避免因为对象过早地被删除而导致的重复释放错误。
3. 确保对象生命周期的正确性确保对象的生命周期正确性需要开发人员仔细地检查和设计代码。
Android内存泄漏研究By Jason Ross1月 6 2015 更新日期:1月 6 2015概念根搜索算法Android虚拟机的垃圾回收采用的是根搜索算法。
GC会从根节点(GC Roots)开始对heap进行遍历。
到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。
根搜索算法相比引用计数法很好的解决了循环引用的问题。
举个例子,Activity有View的引用,View也有Activity的引用,之前我还尝试去源代码里找Activity何时和View断开连接是大错特错了。
当Activity finish掉之后,Activity和View的循环引用已成孤岛,不再引用到GC Roots,无需断开也会被回收掉。
内存泄漏Android内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。
无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。
场景类的静态变量持有大数据对象静态变量长期维持到大数据对象的引用,阻止垃圾回收。
非静态内部类的静态实例非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。
资源对象未关闭资源性对象如Cursor、File、Socket,应该在使用后及时关闭。
未在finally中关闭,会导致异常情况下资源对象未被释放的隐患。
注册对象未反注册未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。
Handler临时性内存泄露Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。
在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。
Android内存泄漏的原因及解决技巧正确的⽣命周期管理如何防⽌Android内存泄漏OutOfMemoryException是⼀个常见的令⼈沮丧的错误,也是导致应⽤程序意外关闭的主要原因之⼀。
“如果应⽤程序昨天运⾏良好,为什么现在会发⽣这种情况?这个问题让Android的开发者和新⼿都感到困惑。
导致OutOfMemory异常的潜在原因有很多种,但其中最常见的是内存泄漏—应⽤程序中的内存分配从未释放。
本⽂将解释如何通过有效的⽣命周期管理(开发过程中⼀个重要但经常被忽视的部分)来最⼩化这种风险。
为什么安卓系统会发⽣内存泄漏?问题很简单。
某些对象应该只有⼀个固定的寿命,当它们的使⽤寿命结束时,它们需要被删除。
理论上,当进程使⽤onStop或onDestroy终⽌时,应该处理该内存。
但是,滥⽤对象引⽤可能会阻⽌垃圾收集器释放未使⽤的对象。
例如:如果未使⽤的对象A引⽤了未使⽤的对象B,那么您将得到两个不必要的对象,垃圾回收器将永远不会释放它们,因为它们正在相互引⽤。
阻⽌内存泄漏这种情况发⽣的常见技巧开发⼈员可以采取许多步骤来阻⽌死的活动被困在内存中。
1. 在onResume()/onPause()或onStart()/onStop()中注册/注销⼴播接收器2. 不要对视图/活动/上下⽂使⽤静态变量3. 需要保存对上下⽂的引⽤的singleton应该使⽤applicationContext()或将其包装到WeakReference中4. 注意匿名和⾮静态内部类,因为它们包含对其封闭类的隐式引⽤。
5. 如果要⽐⽗类(如处理程序)更长寿,请使⽤静态内部类⽽不是匿名类。
6. 如果内部或匿名类是可取消的(如AsyncTask、Thread、RxSubscriptions),则在销毁活动时取消它。
Android⽣命周期感知组件⼀旦你完成了上⾯的基本步骤,现在是时候做⼀些更重要的事情了:应⽤程序活动的⽣命周期。
如果我们不能正确地管理⽣命周期,我们最终会在不再需要内存的时候挂掉它。
检测内存泄露的方法
1. 手动检查代码:内存泄漏通常是由于程序未正确释放动态分配的内存造成的,因此,开发人员可以手动审查他们的代码,以确保内存管理的正确性。
2. 静态代码分析工具:静态代码分析工具(如PVS-Studio、Coverity等)可以检测代码中的潜在问题和内存泄漏。
他们分析代码以查找未释放的内存和其它资源。
3. 动态代码分析工具:动态代码分析工具(如Valgrind、Dr.Memory等)可以模拟应用程序的执行,并跟踪内存的分配和释放。
这些工具可以检测内存泄漏和其它内存管理问题。
4. 内存分析工具:内存分析工具(如Heap Profiler、Memory Analyzer等)可以帮助开发人员识别内存泄漏并找到其原因。
他们可以跟踪内存分配和释放,并生成详细的报告,以帮助开发人员定位问题。
5. 内存泄漏检测工具:内存泄漏检测工具(如LeakCanary等)专门用于检测Android平台上的内存泄漏。
他们可以在应用程序中检测出未释放的对象,并
提供详细的报告和堆栈跟踪,以帮助开发人员找到问题所在。
android操作系统中的内存溢出的工作原理概述说明1. 引言1.1 概述本文将对Android操作系统中的内存溢出工作原理进行深入探讨。
在Android 开发中,内存溢出是一个普遍存在的问题,严重影响着应用程序的性能和稳定性。
了解内存溢出的工作原理可以帮助开发者更好地理解和解决这一问题。
1.2 文章结构本文将分为五个部分来进行详细说明。
首先,我们将从概述开始介绍本文的目的和结构。
然后,我们将进入第二部分,介绍Android内存管理的概述。
接着,在第三部分中,我们将深入探讨内存溢出的定义与原因以及与内存泄漏的区别。
第四部分将详细阐述内存溢出的发生机制和示例分析,并总结导致内存溢出引起的问题以及解决方法。
最后,在第五部分,我们会对全文进行总结,并展望未来该领域的发展方向和关注点。
1.3 目的本文旨在全面了解Android操作系统中内存溢出的工作原理,并为开发者提供解决方案,以应对因此产生的常见问题。
通过深入研究Android内存管理机制、GC(垃圾回收)机制以及常见的内存泄漏场景,我们将能够更好地理解内存溢出的原因与机理,从而优化应用程序的性能和稳定性。
同时,展望未来的发展方向和关注点,有助于开发者在不断变化的技术环境中保持敏锐的观察力并跟进最新的解决方案。
2. Android操作系统中的内存溢出工作原理:2.1 Android内存管理概述:在Android操作系统中,内存管理是一个非常重要且复杂的任务。
由于移动设备的资源有限,包括RAM和存储容量都是相对较小的,所以内存管理必须高效地利用可用的资源。
Android采用了一种基于Linux内核的分层架构,在其顶层运行着Java虚拟机(JVM),负责处理应用程序和系统之间的交互,并提供了一个统一的应用程序开发框架。
2.2 内存溢出定义与原因:内存溢出指的是在程序运行过程中占用的内存超过了可用内存资源所导致的问题。
这种情况通常是由以下几个主要原因引起的:- 对象未被正确释放:在Java语言中,垃圾回收机制(Garbage Collection)负责自动释放不再使用的对象。
分析一个内存泄露:首先进入App,什么都不做,等待10分钟。
查看大概的内存占用根据HPROF文件,用MAT分析以上的分析中,前三个疑点,分别是:1android.content.res.Resources并非真正泄露,只是Resources本身的缓存机制。
即缓存了8,526,008 (35.61%) bytes 2android.graphics.Bitmap所属MapActivity3android.graphics.Bitmap所属com.autonavi.minimap.Splashy以第三为例分析:One instance of "android.graphics.Bitmap" loaded by "<system class loader>" occupies 2,731,584 (11.41%) bytes. The memory is accumulated in one instance of "byte[]" loaded by "<system class loader>".说明有Bitmap没有释放,占用了2.6M,存在泄露可能。
然后看截图。
分析上图,发现泄露的原因是com.autonavi.minimap.Splashy没有释放。
此Splashyde的实例被引用于com.autonavi.navi.tools.AutoNaviEngine。
然后AutoNaviEngine的实例被引用于自己和com.autonavi.navi.FrameForTBT。
一直没有释放。
查看代码:com.autonavi.minimap.Splashy:publicvoid onCreate(Bundle savedInstanceState) {…AutoNaviEngine.getInstance().initNaviEngine(this);将自己(Activity)传给AutoNaviEngine,…}com.autonavi.navi.tools.AutoNaviEngineprivatestatic AutoNaviEngine instance = null;public Context context = null;publicvoid initNaviEngine(Context context) {…fromForTbt = new FrameForTBT(this, tbt);…setNaviEngineContext(context);此context就是Splashy,作为static instance的引用,不会被释放。
Android内存泄漏的原因什么是内存泄漏?在了解Android内存泄漏的原因之前,我们首先需要理解什么是内存泄漏。
简单来说,内存泄漏指的是在程序运行过程中,由于某些原因导致无用的对象无法被垃圾回收机制回收而一直占据着内存空间,最终导致可用内存逐渐减少甚至耗尽。
这会导致应用程序变得不稳定、卡顿甚至崩溃。
内存泄漏的原因1. 静态引用导致的内存泄漏静态引用是指被声明为静态变量或静态方法中使用的对象引用。
当一个对象被赋给一个静态变量时,它将一直存在于内存中直到应用程序退出。
如果该对象持有其他对象的引用,并且这些对象也被声明为静态变量,则会形成一个“对象网”(object graph)。
即使这些对象在实际上已经不再使用,但它们仍然占据着内存空间,从而造成了内存泄漏。
解决方法:避免过度使用静态引用,并及时释放不再需要的静态变量。
2. 非静态内部类的隐式引用导致的内存泄漏非静态内部类会默认持有外部类的引用,这意味着如果一个非静态内部类的实例被持有,并且生命周期比外部类长,那么外部类就无法被垃圾回收。
这种情况下,如果非静态内部类持有外部类中大量数据或对象的引用,就会造成内存泄漏。
解决方法:将非静态内部类改为静态内部类,或者使用弱引用(WeakReference)来持有外部类的引用。
3. 匿名内部类导致的内存泄漏匿名内部类是指没有命名的、直接作为参数传递给其他方法或构造函数的内部类。
由于匿名内部类会默认持有外部类的引用,所以如果匿名内部类实例被保留下来并且生命周期比外部类长,就会导致外部类无法被垃圾回收。
解决方法:避免在需要长时间保持生命周期的情况下使用匿名内部类,或者使用弱引用来持有外部对象。
4. Handler导致的内存泄漏Handler是Android中常用于在不同线程之间通信和处理消息的机制。
然而,在使用Handler时,如果Handler对象持有一个Activity的引用,并且该Activity已经被销毁但仍然存在于消息队列中,就会导致Activity无法被回收,从而造成内存泄漏。
安卓内存泄漏的原因1.对象引用未释放:在Android中,每个Activity和Fragment等都使用Java对象进行表达。
当一个Activity或Fragment实例被销毁时,如果有其他对象仍然对它持有引用,那么这个实例将无法被垃圾回收机制回收,从而造成内存泄漏。
常见的引起内存泄漏的情况包括:单例模式、静态变量、匿名内部类等。
2. Handler引起的内存泄漏:Handler对象通常会与Activity或Fragment关联,如果在Worker Thread中创建Handler,而且Message会被延迟发送,这时如果Activity或Fragment已经被销毁,但是Handler仍然持有对其的引用,将导致Activity或Fragment无法被垃圾回收。
3.资源未关闭:在Android开发中经常使用各种资源,如数据库、网络连接、文件IO等。
如果在使用完这些资源后没有正确关闭,将会导致资源泄漏。
比如,打开数据库连接后,忘记关闭连接;打开文件流后,没有调用close 方法等。
4.非静态内部类引起的内存泄漏:非静态内部类的实例会隐式持有外部类的引用,如果该内部类的实例长时间存在或者被其他对象引用,而外部类被销毁了,那么外部类无法被垃圾回收,进而引发内存泄漏。
5. Bitmap引起的内存泄漏:Bitmap对象占用内存较大,在使用时需要及时释放。
如果Bitmap对象被加载到内存中后没有及时调用recycle方法释放内存,将会造成内存泄漏。
6.注册监听器引起的内存泄漏:在使用注册监听器时,如果没有在适当的时候解注册,就会导致引发内存泄漏。
比如使用广播接收器需要在Activity销毁时解注册。
7.资源缓存引起的内存泄漏:在需要频繁使用的资源中,为了提高效率可能会进行缓存。
但如果没有适当控制缓存的大小,或者缓存中的对象没有被正确释放,就会导致内存泄漏。
为了避免安卓内存泄漏,可以采取以下措施:1. 避免使用静态变量、单例模式等方式持有Context或其他与Activity或Fragment相关的对象;2. 使用弱引用(WeakReference)来持有Activity或Fragment;3. 在Activity或Fragment销毁时,及时关闭资源、解注册广播接收器等;4. 在使用Handler时,使用静态内部类并使用弱引用持有Activity 或Fragment;5. 压缩图片、及时回收Bitmap对象;6.在适当的时候清理资源缓存。
利用Android Studio、MAT对Android进行内存泄漏检测Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的crash,甚至可能出现因内存不足而导致APP崩溃。
内存泄漏分析工具Android的内存泄漏分析工具常用有Android Studio和基于eclipse的MAT (Memory Analyzer Tool)。
通过两者配合,可以发挥出奇妙的效果。
Android Studio能够快速定位内存泄漏的Activity,MAT能根据已知的Activity快速找出内存泄漏的根源。
第一步:强制GC,生成Java Heap文件我们都知道Java有一个非常强大的垃圾回收机制,会帮我回收无引用的对象,这些无引用的对象不在我们内存泄漏分析的范畴,Android Studio有一个Android Monitors帮助我们进行强制GC,获取Java Heap文件。
强制GC:点击Initate GC(1)按钮,建议点击后等待几秒后再次点击,尝试多次,让GC更加充分。
然后点击Dump Java Heap(2)按钮,然后等到一段时间,生成有点慢。
生成的Java Heap文件会在新建窗口打开。
第二步:分析内存泄漏的Activity点击Analyzer Tasks的Perform Analysis(1)按钮,然后等待几秒十几秒不等,即可找出内存泄漏的Activity(2)。
那么我们就可以知道内存泄漏的Activity,因为这个例子比较简单,其实在(3)就已经可以看到问题所在,如果比较复杂的问题Android Studio并不够直观,不够MAT方便,如果Android Studio无法解决我们的问题,就建议使用MAT来分析,所以下一步我们就生成标准的hprof文件,通过MAT来找出泄漏的根源。
第三步:转换成标准的hprof文件刚才生成的Heap文件不是标准的Java Heap,所以MAT无法打开,我们需要转换成标准的Java Heap文件,这个工具Android Studio就有提供,叫做Captures,右击选中的hprof,Export to standard .hprof选择保存的位置,即可生成一个标准的hprof文件。
MAT概述对于大型JAVA 应用程序来说,再精细的测试也难以堵住所有的漏洞,即便我们在测试阶段进行了大量卓有成效的工作,很多问题还是会在生产环境下暴露出来,并且很难在测试环境中进行重现。
JVM 能够记录下问题发生时系统的部分运行状态,并将其存储在堆转储(Heap Dump) 文件中,从而为我们分析和诊断问题提供了重要的依据。
通常内存泄露分析被认为是一件很有难度的工作,一般由团队中的资深人士进行。
不过,今天我们要介绍的MAT(Eclipse Memory Analyzer)被认为是一个“傻瓜式“的堆转储文件分析工具,你只需要轻轻点击一下鼠标就可以生成一个专业的分析报告。
和其他内存泄露分析工具相比,MAT 的使用非常容易,基本可以实现一键到位,即使是新手也能够很快上手使用。
MAT 的使用是如此容易,你是不是也很有兴趣来亲自感受下呢,那么第一步我们先来安装MAT。
准备环境和测试数据我们使用的是Eclipse Memory Analyzer V1.2.0,JDK 6安装MAT和其他插件的安装非常类似,MAT 支持两种安装方式,一种是“单机版“的,也就是说用户不必安装Eclipse IDE 环境,MAT 作为一个独立的Eclipse RCP 应用运行;另一种是”集成版“的,也就是说MAT 也可以作为Eclipse IDE 的一部分,和现有的开发平台集成。
1、单机版安装方式单机版可到可到/mat/downloads.php下载,解压运行后如下图所示:在MAT中打开获取的转储文件,文件夹在完成后自动生成分析报告,如图所示主工作区展示文件分析的概况,可以看到主要的类占用(Biggest Objects)和可选的一些操作,Action,Reports等。
左侧的Inspector区可以查看各个类的域信息,如图显示的是一个com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate类实例的信息,包括其各个域的值。
内存泄漏测试与排查方法内存泄漏是软件开发过程中常见的问题之一,它会导致程序运行变慢、占用过多的系统资源,最终可能引发系统崩溃或应用程序崩溃。
因此,进行内存泄漏测试和排查方法是很重要的。
内存泄漏测试是为了发现应用程序中存在的内存泄漏问题。
下面将介绍一些常用的内存泄漏测试方法。
第一种方法是静态分析。
静态分析是通过检查源代码中可能导致内存泄漏的部分来判断内存泄漏问题。
可以使用静态代码分析工具来辅助进行该项测试。
这些工具可以帮助开发人员发现潜在的内存泄漏问题,如资源未释放、循环引用等。
在测试中,我们可以使用这些工具扫描应用程序的源代码,找出可能存在内存泄漏的地方,并及时修复。
第二种方法是动态分析。
动态分析是通过运行应用程序并监控其内存使用情况来检测内存泄漏。
在这种方法中,我们使用各种性能分析工具来监视应用程序的内存使用情况,例如内存分配和释放的次数、内存泄漏的对象等。
通过分析这些数据,我们可以确定是否存在内存泄漏问题,并找到导致内存泄漏的具体原因。
动态分析是一种非常常用和有效的方法,可以在应用程序运行时发现内存泄漏问题,并及时采取措施进行修复。
第三种方法是使用内存检测工具。
内存检测工具可以帮助开发人员检测内存泄漏问题,并提供详细的报告来指导修复。
这些工具可以监视应用程序运行时的内存分配和释放情况,并检查是否有未释放的内存块。
一旦发现内存泄漏问题,工具会生成相应的报告,指导开发人员进行修复。
常用的内存检测工具包括Valgrind、Memcheck等。
在排查内存泄漏问题时,我们需要注意以下几点。
定位内存泄漏问题。
通过使用上述方法检测和分析应用程序的内存使用情况,确定是否存在内存泄漏问题。
可以通过追踪对象的创建和销毁、监控内存的分配和释放等方法来定位问题。
分析内存泄漏原因。
一旦确定了存在内存泄漏问题,我们需要深入分析其原因。
可能的原因包括资源未正确释放、循环引用等。
修复内存泄漏问题。
根据分析结果,采取相应的措施进行修复。
Android 应用程序内存泄漏的分析
以前在学校里学习Java的时候,总是看到说,java是由垃圾收集器(GC)来管理内存回收的,
所以当时形成的观念是Java不会产生内存泄漏,我们可以只管去申请内存,不需要关注内存回收,
GC会帮我们完成。呵呵,很幼稚的想法,GC没那么聪明啊,理论及事实证明,我们的Java程序也
是会有内存泄漏的。
(一) Java内存泄漏从何而来
一般来说内存泄漏有两种情况。一种情况如在C/C++语言中的,在堆中的分配的内存,没
有将其释放,或者是在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指
针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它
的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解
决。所以,Java中的内存泄漏,主要指的是第二种情况。
(二) 需要的工具
1. DDMS—Update heap Gause GC
Heap 是DDMS自带的一个很不错的内存监控工具,下图红色框中最左边的图标就是该
工具的启动按钮,它能在Heap视图中显示选中进程的当前内存使用的详细情况。下图
框中最右边的是GC工具,很多时候我们使用Heap监控内存的时候要借助GC工具,点
击一次GC按钮就相当于向VM请求了一次GC操作。中间的按钮是Dump HPROF file,它
的功能相当于给内存拍一张照,然后将这些内存信息保存到hprof文件里面,在使用我
们的第二个工具MAT的时候会使用到这个功能。
2. MAT(Memory Analyzer Tool)
Heap工具能给我们一个感性的认识,告诉我们程序当前的内存使用情况和是否存在内存
泄漏的肯能性。但是,如果我们想更详细,更深入的了解内存消耗的情况,找到问题所
在,那么我们还需要一个工具,就是MAT。这个工具是需要我们自己去下载的,可以下
载独立的MAT RCP 客户端,也可以以插件的形式安装到Eclipse里面,方便起见,推荐
后者。
安装方法:
A. 登录官网http://www.eclipse.org/mat/downloads.php
B. 下载MAT Eclipse插件安装包(红框所示,当然你也可是选择Update Site在线安装,
个人觉得比较慢)
C. 安装
在Eclipse里面安装新软件,选择刚才下载的本地安装包进行安装
(三) 案例分析
工具准备好了,那就来看看怎么使用。我们以Q+ for Pad为例,看看查找好友功能是否存在
内存泄漏。
1. 打开 eclipse 并切换到 DDMS 透视图,同时确认 Devices 、 Heap 和 logcat 视图已经打
开了。
2. 将Pad设备链接到电脑,并确保使用“ USB 调试”模式链接
3. 启动我们的Q+ for Pad应用,此时我们能看到下图所示的情况,Q+ Pad有两个进程
4. 选中main进程,点击Update Heap按钮,再点击GC按钮,查看该进程当前堆内存的使用
情况
如何才能知道我们的程序是否有内存泄漏的可能性呢。这里需要注意一个值:Heap视
图中部有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类
型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java
数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样
判断:
a) 不断的操作当前应用,同时注意观察data object的Total Size值;
b) 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代
码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成
很多对象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会
会落到一个稳定的水平;
c) 反之如果代码中存在没有释放对象引用的情况,则data object的Total Size值在每
次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,直到到
达一个上限后导致进程被kill掉。
5. 输入昵称,查找联系人,在查到的结果中不断向下翻页,最后退出(这个时候程序会不
断的拉取联系人的头像,这是我们要关注的),然后我们点击下GC按钮,手动触发下垃
圾回收,结果截图如下,图中红色标记的地方可以看出,执行查找联系人操作后,这两
个数值明显增加了,GC操作也无法使之下降,我们可以怀疑,这个操作导致了内存泄漏。
6. 使用MAT进一步分析,找到问题的根源。之前我们已经安装了MAT插件,所以这里我
们只要选中main进程,点击Dump HPROF file按钮,就会跳转到MAT视图。在弹出的对
话框中选择报告类型,一般选第一个就行。
点击完成后,MAT会自动生成报告,列出几个内存占用比较大的可疑对象,MAT不会明
确告诉我们这就是泄露,因为它也不知道这个东西是不是程序还需要的,只有程序员自
己知道。图中很明显的占用内存较大的是头像资源,并且当退出查找联系人功能后,这
些资源是需要释放的,陌生人的头像我们不需要缓存在内存里面。
点击Domain Tree按钮,可以按包进行分组。点开树形列表,可以更详细的看到类对象
占用内存的大小。其中,Shallow Heap表示实例的内存使用总和,Retained Heap表示所
有类实例被分配的内存总和,里面也包括他们所有引用的对象。
从生成的数据中,我们发现有一千多个HashMapEntry对象,针对单个HashMapEntry对
象继续追踪,最后找到了一个6KB左右的Bitmap,应该就是我们的头像资源。
因此,为了回收内存,我们必须把刚才查找好友保存在内存中的头像资源释放。通过使
用Heap 和MAT工具,能够更好的帮助测试人员发现及定位内存泄漏问题,也能帮助我
们发下性能问题,找到内存优化的点。