引起Android内存泄露有很多种原因,下面罗列了一些问题,以后会一一解决

1、构造Adapter时没有使用缓存convertView(衍生出ListView优化问题)

2、查询数据库游标没有关闭

3、Activity中生命周期对象大于Activity生命周期(关于Application Context与Activity Context)

4、Bitmap对象不使用时没有recycle掉(这里还有其他解决方案)

今天说的是第一种:如何使用缓存来优化ListView

因为如果不使用缓存convertView的话,调用getView时每次都会重新创建View,这样之前的View可能还没有销毁,加之不断的新建View势必会造成内存泄露。

使用getView时有3方案:(1)没有使用convertView,(2)使用convertView,(3)使用convertView+静态类ViewHolder

我做了一个测试,代码在下面,创建2000个View,从0拉到最后,计算总共耗,同时显示GC释放内存的大小,三种测试的结果如下:

注:这里先说下 GC_EXTERNAL_ALLOC freed 7K, 18% free 11153K/13511K, external 1632K/1672K, paused 89ms 的意思

  在Dalvik中,为一个程序分配的内存要根据机型的不同而不同,一般为32M,而虚拟机会把这些内存分别分配给,JAVA使用的堆内存(heap)和Nativie使用的内存(external)(即虚拟机中通过JNI调用本地Nativie的类中malloc分配的内存,如Bitmap,java.nio.ByteBuffers)。不过两者不同共享,也就是说Native的内存不够用了,而JAVA内存够用时是不能向JAVA申请的,必须向虚拟机申请才行,当虚拟机无法分配的时候就会报OOM的错误

freed 7k:表示GC已经释放了7K的内存

18% free 11153K/13511K:表示JAVA使用的堆内存(对象存在于此),18% free表示当前剩余18%的堆内存(heap memory),11153K表示当前已用的堆内存,13511K表示堆内存总共大小(网上有些文章这部分弄错了,很多转载都是同一个)

external 1632K/1672K:1632K表示已用external memory,总共1672K external memory(注意:这个可能只存在于Android 3.0之前)

paused 89ms:这里其实包括了两部分,一个是在调用GC之前暂停的时间,一个是调用GC后基本完成时暂停的时间

详细可参考:http://stackoverflow.com/questions/4550757/android-logs-gc-external-alloc-gc-for-malloc

(1)没有使用convertView

  没有任何处理,不建议这样写。如果数据量少可以,但是如果列表项数据量很大的时候,会每次都重新创建View,设置资源,严重影响性能,所以从一开始就不要用这种方式

@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Get a View that displays the data at the specified position in the data set.
//开始计时,性能测试用nanoTime会更精确,因为它是纳秒级的
long startTime = System.nanoTime();
View item = mInflater.inflate(R.layout.list_item, null);
ImageView img = (ImageView)item.findViewById(R.id.img);
TextView title = (TextView)item.findViewById(R.id.title);
TextView info = (TextView)item.findViewById(R.id.info);
img.setImageResource(R.drawable.ic_launcher);
title.setText("loulijun");
info.setText("www.cnblogs.com/loulijun"); //停止计时
long endTime = System.nanoTime();
//耗时
long spendTime = (endTime - startTime); sumTime += spendTime;
Log.d("GoogleIO", "position at:"+position+"--sumTime:"+String.valueOf(sumTime));
return item;
}

测试结果:

目前VM只为他们分配了5767K+518k的内存,而内存峰值是32M

刚开始时,而且heap memory只申请了5767K,已用内存3353K,注意数据大小的变化:耗时:167633055ns = 0.167633055秒

当拉到1000的时候,堆内存总计已经申请了9607K,已用内存7245K,明显已经比刚开始时要大了 ,耗时:3435241667ns=3.435241667秒

当拉到2000的时候,堆内存总计13511K,已用内存11153K,耗时:6660369835ns = 6.660369835秒

---------------------------我又创建了10000个ListView,测试后直到内存泄露,证明峰值却是是32M,而不使用convertView导致的内存泄露,当内存泄露时手机会提示force close,并将错误写入/data/anr/traces.txt中,你可以adb pull下来查看具体信息

(2)使用convertView后的测试数据(优化后)

  通过缓存convertView,convertView可以缓存可视范围内的convertView,当再次向下滑动时又开始更新View,这种利用缓存convertView的方式可以判断如果缓存中不存在View才创建View,如果已经存在可以利用缓存中的View,这样会减少很多View的创建,提升了性能

@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Get a View that displays the data at the specified position in the data set.
if(convertView == null)
{
convertView = mInflater.inflate(R.layout.list_item, null);
}
//开始计时,性能测试用nanoTime会更精确,因为它是纳秒级的
long startTime = System.nanoTime(); ImageView img = (ImageView)convertView.findViewById(R.id.img);
TextView title = (TextView)convertView.findViewById(R.id.title);
TextView info = (TextView)convertView.findViewById(R.id.info);
img.setImageResource(R.drawable.ic_launcher);
title.setText("loulijun");
info.setText("www.cnblogs.com/loulijun"); //停止计时
long endTime = System.nanoTime();
//耗时
long spendTime = (endTime - startTime); sumTime += spendTime;
Log.d("GoogleIO", "position at:"+position+"--sumTime:"+String.valueOf(sumTime));
return convertView;
}

测试数据我还是用2000吧,10000太大了(一万年太久,只争朝夕)

测试结果:

这次一直拉到最后明显比刚才流畅多了,而且GC释放内存的次数也明显少了很多,最后用的时间和当前使用的内存也小很多,优化后的确好多了

当position为1000的时候,附近没怎么调用GC,用时:213653551ns=0.213653551秒,额,差距有点大,上面到达1000时用时达到3.43秒之多。

当position为2000的时候,已用内存只有3068K,堆总共内存6215K,而且external memory是0K,用时:378326396ns = 0.378326396秒,性能差距如此之大,都有点不敢相信。也不知道这种方式对不对,如有不妥的地方,还希望大牛能给出正确回答

(3)使用contentView+静态类ViewHolder类

  通过convertView+ViewHolder来实现,ViewHolder就是一个静态类,使用 ViewHolder 的关键好处是缓存了显示数据的视图(View),加快了 UI 的响应速度。

当我们判断 convertView == null  的时候,如果为空,就会根据设计好的List的Item布局(XML),来为convertView赋值,并生成一个viewHolder来绑定converView里面的各个View控件(XML布局里面的那些控件)。再用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。(看下面代码中)

如果convertView不为空的时候,就会直接用convertView的getTag(),来获得一个ViewHolder。

静态类ViewHolder

//定义静态类ViewHolder
static class ViewHolder
{
public ImageView img;
public TextView title;
public TextView info;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Get a View that displays the data at the specified position in the data set. //开始计时,性能测试用nanoTime会更精确,因为它是纳秒级的
long startTime = System.nanoTime();
ViewHolder holder; if(convertView == null)
{
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null);
holder.img = (ImageView)convertView.findViewById(R.id.img);
holder.title = (TextView)convertView.findViewById(R.id.title);
holder.info = (TextView)convertView.findViewById(R.id.info);
convertView.setTag(holder);
}else
{
holder = (ViewHolder)convertView.getTag();
holder.img.setImageResource(R.drawable.ic_launcher);
holder.title.setText("loulijun");
holder.info.setText("www.cnblogs.com/loulijun");
} //停止计时
long endTime = System.nanoTime();
//耗时
long spendTime = (endTime - startTime); sumTime += spendTime;
Log.d("GoogleIO", "position at:"+position+"--sumTime:"+String.valueOf(sumTime));
return convertView;
}

到这里,可能会有人问ViewHolder静态类结合缓存convertView与直接使用convertView有什么区别吗,是否重复了

在这里,官方给出了解释

提升Adapter的两种方法

To work efficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView() to avoid inflating View when it is not necessary

(译:重用缓存convertView传递给getView()方法来避免填充不必要的视图)
-It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary

(译:使用ViewHolder模式来避免没有必要的调用findViewById():因为太多的findViewById也会影响性能)
ViewHolder类的作用
-The ViewHolder pattern consists in storing a data structure in the tag of the view
returned by getView().This data structures contains references to the views we want to bind data to,
thus avoiding calling to findViewById() every time getView() is invoked

(译:ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们

要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById())

测试数据:(跟直接使用convertView数据相差不多)

当position为1000时,用时:199188216ns = 0.199188216秒,堆内存的时候也没比没有使用convertView理想的多

当position为2000时,用时:336669887ns = 0.336669887秒,比直接使用convertView的方式稍微好一点点,不过性能相差不多

内存泄露--contentView缓存使用与ListView优化的更多相关文章

  1. Android常见内存泄露,学会这六招优化APP性能

    很多开发者都知道,在面试的时候会经常被问到内存泄露和内存溢出的问题. 1.内存溢出(Out Of Memory,简称 OOM),通俗理解就是内存不够,即内存占用超出内存的空间大小. 2.内存泄漏(Me ...

  2. dotnet 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露

    本文记录我将应用迁移到 dotnet 6 之后,在 Win7 系统上,因为使用 HttpWebRequest 访问一个本地服务,此本地服务开启 https 且证书链在此 Win7 系统上错误,导致应用 ...

  3. android:布局、绘制、内存泄露、响应速度、listview和bitmap、线程优化以及一些优化的建议!

    1.布局优化 首先删除布局中无用的控件和层级,其次有选择地使用性能较低的viewgroup,比如布局中既可以使用RelativeLayout和LinearLayout,那我们就采用LinearLayo ...

  4. Android为TV端助力 布局、绘制、内存泄露、响应速度、listview和bitmap、线程优化以及一些优化的建议!

    1.布局优化 首先删除布局中无用的控件和层级,其次有选择地使用性能较低的viewgroup,比如布局中既可以使用RelativeLayout和LinearLayout,那我们就采用LinearLayo ...

  5. Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...

  6. Android性能优化——之防止内存泄露

    又是好久没有写博客了,一直都比较忙,最近终于有时间沉淀和整理一下最近学到和解决的一些问题. 最近进行技术支持的时候,遇到了几个崩溃的问题,都是OOM异常,一般OOM异常给人的感觉应该是加载大图片造成的 ...

  7. Flex内存泄露解决方法和内存释放优化原则

    Flex内存泄露解决方法和内存释放优化原则 你对Flex内存泄露的概念是否了解,这里和大家分享一下Flex内存释放优化原则和Flex内存泄露解决方法,希望本文的介绍能让你有所收获. Flex内存释放优 ...

  8. Android性能优化:手把手带你全面了解 内存泄露 & 解决方案

    . 简介 即 ML (Memory Leak)指 程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象2. 对应用程序的影响 容易使得应用程序发生内存溢出,即 OOM ...

  9. Android内存优化7 内存检测工具1 Memory Monitor检测内存泄露

    上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过Android Studio自带工具Memory Monitor 检测出 ...

随机推荐

  1. hadoop各组件安装(非专业人士,不定期更新)

    压缩包下载http://www.cnblogs.com/bfmq/p/6027202.html 1.zookeepermkdir /usr/local/hadooptar zxf /root/zook ...

  2. <正则吃饺子> :关于Guava中 Joiner 和 Splitter 的简单使用

    在现在项目中经常看到 这两个类的使用,开始时候不明白具体是做的什么事情,就单独拿出来学习下了,参照了网上的博文,这里主要是简单的讲讲用法. 具体对这两个类,不做过多介绍,有个在线文档,需要的可以自己去 ...

  3. 关于chrome被篡改主页修复方法

    打开chrome的属性. 在目标框中追加以下内容 "PATH\chrome.exe" --flag-switches-begin --flag-switches-end about ...

  4. Paint Tree

    题意: 给定一棵n个点的树,给定平面上n个点,将n个点用线段连起来画成树的形状,使得不存在不在端点相交的线段,构造出一种情况. 解法: 首先观察我们常规画出来的树形图可知,树的子树是根据极角分开的,这 ...

  5. JS正则表达式(一)

    正则表达常用符号 /..../  开始结束 ^ 开始 $ 结束 /s 任何非空字符  /S 非空 /d  匹配一个数字=[0-9] /D  匹配一个非数字=[^0-9] /w   匹配一个数字,下划线 ...

  6. HTML学习笔记(六)TCP/IP

    TCP/IP 是供已连接因特网的计算机进行通信的通信协议. 在 TCP/IP 中包含一系列用于处理数据通信的协议: TCP (传输控制协议) - 应用程序之间通信 UDP (用户数据包协议) - 应用 ...

  7. 1 手写Java ArrayList核心源码

    手写ArrayList核心源码 ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet,Queue ...

  8. E20180512-hm

    travesal n. 横越,横断物,(横向)往返移动;

  9. lightoj1064 【DP求方案】

    题意: n个相同的骰子,问你掷出>=x点数的可能性: 思路: dp[i][j]代表前 i 个骰子掷出 j 点数的方案数; 然后Σdp[n][x]-dp[n][6*n]就好了 卧槽,一开始想的是拆 ...

  10. CZGL.AliIoTClient 文档:说明

    文档目录: 说明 1. 连接阿里云物联网 2. IoT 客户端 3. 订阅Topic与响应Topic 4. 设备上报属性 4.1 上报位置信息 5. 设置设备属性 6. 设备事件上报 7. 服务调用 ...