最经开发app使出现了由于ListView产生的内存泄露问题。咱们知道内存泄露时很是很差的。java
意味着。代码写的有点失败。需要作些优化修改。android
通过此次的教训,以及在网上找了些资料。总结了一下。关于ListView的优化:面试
listview优化问题:
首先。listview必须严格依照convertView及viewHolder格式书写,这样可以基本保证数据最优。数据库
其次,假设本身定义Item中有涉及到图片等等的,必定要作图片优化。bitmap释放可以不作。缓存
第三。尽可能避免在BaseAdapter中使用static 来定义全局静态变量,这个影响很是大。static是Java中的一个keyword。app
当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。因此用static修饰的变量,它的生命周期是
很是长的。假设用它来引用一些资源耗费过多的实例(比方Context的状况最多),这时就要尽可能避免使用了..异步
第四,尽可能避免在ListView适配器中使用线程,因为线程产生内存泄露的主要缘由在于线程生命周期的不可控制。jvm
最后,假设上述你都作到的话。你的listview已经优化的很是好了。针对你的问题,你的listview控件高度是否
设置为fill_parent。因为wrap会致使listview滑动中无限计算自身高度。你的文本载入是否作过线程以及多
次反复载入的问题处理。你的item中变量是否屡次无限生成新的内存对象等等。函数
咱们常常在作项目过程当中遇到内存溢出的问题,同一时候面试中关于OOM的问题也常常出现。
这里。我将前辈们解决Andorid内存溢出的方法又一次整理一番,方便本身之后使用。
1、Android的内存机制
android应用层是由java开发的,android的davlik虚拟机与jvm也相似。仅仅只是它是基于寄存器的。在java中,经过new为对象分配内存,所有对象在java堆内分配空间。而内存的释放是由垃圾收集器(GC)来回收的。 Java採用了有向图的原理。工具
Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。线程对象可以做为有向图的起始顶点。该图就是从起始顶点(GC roots)開始的一棵树,根顶点可以到达的对象都是有效对象。GC不会回收这些对象。假设某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么咱们以为这个(这些)对象再也不被引用。可以被GC回收。
2、Android的内存溢出缘由
一、内存泄露致使
由于咱们程序的失误。长期保持某些资源(如Context)的引用,垃圾回收器就没法回收它,固然该对象占用的内存就没法被使用,这就形成内存泄露。
Android 中常见就是Activity被引用在调用finish以后却没有释放,第二次打开activity又又一次建立,这种内存泄露不断的发生,则会致使内存的溢出。
Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来执行。它是由Zygote服务进程孵化出来的,也就是说每个应用程序都是在属于本身的进程中执行的。Android为不一样类型的进程分配了不一样的内存使用上限。假设程序在执行过程当中出现了内存泄漏的而形成应用进程使用的内存超过了这个上限。则会被系统视为内存泄漏,从而被kill掉。这使得只本身的进程被kill掉,而不会影响其它进程.
二、占用内存较多的对象
保存了多个耗用内存过大的对象(如Bitmap)或载入单个超大的图片。形成内存超出限制。
3、常见的内存泄漏问题及其解决方式
一、引用没释放形成的内存泄露
1.1注冊没取消形成的内存泄露
这样的Android的内存泄露比纯Java的内存泄漏还要严重,因为其它一些Android程序可能引用系统的Android程序的对象(比方注冊机制)。即便Android程序已经结束了。但是别的应用程序仍然还有对Android程序的某个对象的引用。泄漏的内存依旧不能被垃圾回收。
1.2集合中对象没清理形成的内存泄露
咱们一般把一些对象的引用增长到了集合中,当咱们不需要该对象时,并无把它的引用从集合中清理掉,这样这个集合就会愈来愈大。假设这个集合是static的话,那状况就更严重了。
1.3 static
static是Java中的一个keyword。当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。
private static ActivitymContext; //省略
怎样才干有效的避免这样的引用的发生呢?
第一,应该尽可能避免static成员变量引用资源耗费过多的实例。比方Context。
第2、Context尽可能使用ApplicationContext,因为Application的Context的生命周期比較长,引用它不会出现内存泄露的问题。
看使用的周期是否在activity周期内,假设超出,必须用application;常见的情景包含:AsyncTask。Thread。第三方库初始化等等。
还有些情景,仅仅能用activity:比方。对话框,各类View,需要startActivity的等。
总之,尽量使用Application。
第3、使用WeakReference取代强引用。
比方可以使用WeakReference<Context>mContextRef;
1.四、线程(内部类的使用)
线程产生内存泄露的主要缘由在于线程生命周期的不可控。假设咱们的线程是Activity的内部类,因此MyThread中保存了Activity的一个引用。当MyThread的run函数没有结束时。MyThread是不会被销毁的,所以它所引用的老的Activity也不会被销毁,所以就出现了内存泄露的问题。
假设非静态内部类的方法中,有生命周期大于其所在类的。那就有问题了。比方:AsyncTask、Handler。这两个类都是方便开发人员运行异步任务的,但是,这两个都跳出了Activity/Fragment的生命周期。
一、
解决方式
第1、将线程的内部类,改成静态内部类。
缘由:
因为非静态内部类会本身主动持有一个所属类的实例,假设所属类的实例已经结束生命周期。但内部类的方法仍在运行,就会hold其主体(引用)。也就使主体不能被释放,亦即内存泄露。静态类编译后和非内部类是同样的,有本身独立的类名。不会悄悄引用所属类的实例,因此就不easy泄露。
第2、假设需要引用Acitivity。使用弱引用。
二、资源对象没关闭形成的内存泄露
资源性对象比方(Cursor,File文件等)每每都用了一些缓冲。咱们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。而不是等待GC来处理。它们的缓冲不只存在于java虚拟机内,还存在于java虚拟机外。假设咱们不过把它的引用设置为null,而不关闭它们。每每会形成内存泄露。
因为有些资源性对象,比方SQLiteCursor(在析构函数finalize(),假设咱们没有关闭它,它本身会调close()关闭),假设咱们没有关闭它。系统在回收它时也会关闭它,但是这种效率过低了。
而且android数据库中对Cursor资源的是又限制个数的,假设不及时close掉,会致使别的地方没法得到。
三、一些不良代码成内存压力
有些代码并不形成内存泄露。但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存,对内存的回收和分配形成很是大影响的,easy迫使虚拟机不得不给该应用进程分配不少其它的内存,形成没必要要的内存开支。
3.1 Bitmap没调用recycle()
Bitmap对象在不使用时,咱们应该先调用recycle()释放内存,而后才设置为null.
尽管recycle()从源代码上看,调用它应该能立刻释放Bitmap的主要内存。但是測试结果显示它并没能立刻释放内存。但是我猜应该仍是能大大的加速Bitmap的主要内存的释放。
3.2 构造Adapter时。没有使用缓存的 convertView
以构造ListView的BaseAdapter为例,在BaseAdapter中提共了方法:
public View getView(int position, View convertView, ViewGroup parent)
来向ListView提供每一个item所需要的view对象。
初始时ListView会从BaseAdapter中依据当前的屏幕布局实例化必定数量的view对象,同一时候ListView会将这些view对象缓存起来。
当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,而后被用来构造新出现的最如下的list item。这个构造过程就是由getView()方法完毕的,getView()的第二个形參 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。
由此可以看出。假设咱们不去使用convertView,而是每次都在getView()中又一次实例化一个View对象的话,即浪费时间,也形成内存垃圾,给垃圾回收添加压力。假设垃圾回收来不及的话。虚拟机将不得不给该应用进程分配不少其它的内存。形成没必要要的内存开支。
4、占用内存较多的对象(图片过大)形成内存溢出及其解决方式
因为Bitmap占用的内存实在是太多了。特别是分辨率大的图片。假设要显示多张那问题就更显著了。
Android分配给Bitmap的大小仅仅有8M。
方法1:等比例缩小图片
有时候。咱们要显示的区域很是小,没有必要将整个图片都载入出来,而仅仅需要记载一个缩小过的图片,这时候可以设置必定的採样率。那么就可以大大减少占用的内存。
1
BitmapFactory.Options options = new BitmapFactory.Options();
2
options.inSampleSize = 2;//图片宽高都为原来的二分之中的一个,即图片为原来的四分之中的一个
尽可能不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,
因为这些函数在完毕decode后,终于都是经过java层的createBitmap来完毕的,需要消耗不少其它内存。
所以,改用先经过BitmapFactory.decodeStream方法,建立出一个bitmap,再将其设为ImageView的 source,
decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完毕decode,
无需再使用java层的createBitmap,从而节省了java层的空间。
方法2:对图片採用软引用。及时地进行recycle()操做
尽管,系统能够确认Bitmap分配的内存终于会被销毁,但是由于它占用的内存过多,因此很是可能会超过java堆的限制。所以。在用完Bitmap时,要及时的recycle掉。recycle并不能肯定立刻就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片能够释放了”。
SoftReference<Bitmap> bitmap;
bitmap = new SoftReference<Bitmap>(pBitmap);
if(bitmap != null){
if(bitmap.get() != null && !bitmap.get().isRecycled()){
bitmap.get().recycle();
bitmap = null;
}
}
方法3: 单个页面。横竖屏切换N次后 OOM
1. 看看页面布局其中有没有大的图片。比方背景图之类的。
去除xml中相关设置,改在程序中设置背景图(放在onCreate()方法中):
1
Drawable bg = getResources().getDrawable(R.drawable.bg);
2
XXX.setBackgroundDrawable(rlAdDetailone_bg);
在Activity destory时注意,bg.setCallback(null); 防止Activity得不到及时的释放。
2. 跟上面方法类似。直接把xml配置文件载入成view 再放到一个容器里。而后直接调用 this.setContentView(View view);避免xml的反复载入。
方法4:在页面切换时尽量少地反复使用一些代码。比方:反复调用数据库,反复使用某些对象等等.....
方法5:Android堆内存也可以自定义大小和优化Dalvik虚拟机的内存
--參考资料:http://blog.csdn.net/wenhaiyan/article/details/5519567
注意:若使用这样的方法:project build target 仅仅能选择 <= 2.2 版本号。不然编译将通只是。
因此不建议用这样的方式。
5、Android中内存泄露监測
内存监測工具 DDMS --> Heap
用法比較简单:
· 选择DDMS视图,并打开Devices视图和Heap视图
· 点击选择要监控的进程,比方:上图中我选择的是system_process
· 选中Devices视图界面上的"update heap" 图标
· 点击Heap视图中的"Cause GC" button(至关于向虚拟机发送了一次GC请求的操做)
在Heap视图中选择想要监控的Type。通常咱们会观察dataobject的 total size的变化。正常状况下total size的值会稳定在一个有限的范围内。也就说程序中的代码良好,没有形成程序中的对象不被回收的状况。
假设代码中存在没有释放对象引用的状况。那么data object的total size在每次GC以后都不会有明显的回落。随着操做次数的添加而total size也在不断的添加。(说明:选择好data object后。不断的操做应用,这样才干够看出total size的变化)。假设totalsize确实是在不断添加而没有回落。说明程序中有没有被释放的资源引用。那么咱们应该怎么来定位呢? Android中内存泄露定位 经过DDMS工具可以推断应用程序中是否存在内存泄漏的问题。那又怎样定位到详细出现故障的代码片断,终于找到问题所在呢?内存分析工具MAT Memory Analyzer Tool攻克了这一难题。MAT工具是一个Eclipse 插件,同一时候也有单独的RCP client,MAT工具的解析文件是.hprof,这个文件存放了某进程的内存快照。MAT工具定位内存泄漏详细位置的方法例如如下: ① 生成.hprof文件。Eclipse中生成.hprof文件的方法有很是多。不一样Android版本号中生成.hprof的方式也稍有区别,但它们整体思路是同样的。咱们在DDMS界面选中想要分析的应用进程,在Devices视图界面上方的一行图标button中,同一时候选中“Update Heap”和“Dump HPROF file”两个button,这时DDMS将会本身主动生成当前选中进程的.hprof文件。 ② 将.hprof 文件导入到MAT工具中,MAT工具会本身主动解析并生成报告,点击“Dominator Tree”button。并按包分组,选择已定义的包类点右键。在弹出的菜单中选择List objects﹥With incoming references,这时会列出所有可疑的类。右键点击某一项,并选择Path to GC Roots﹥excludeweak/soft references。MAT工具会进一步筛选出跟程序相关的所有内存泄漏的类。这样就可以追踪到某一个产生内存泄漏的类的详细代码中。 使用MAT内存分析工具查找内存泄漏的根本思路是找到哪一个类的对象的引用没有被释放,而后分析没有被释放的缘由,终于定位到代码中哪些片断存在着内存泄漏。