Android杂谈--内存泄露(1)--contentView缓存使用与ListView优化
引起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("");
//停止计时
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("");
//停止计时
long endTime = System.nanoTime();
//耗时
long spendTime = (endTime - startTime);
&
补充:移动开发 , Android ,