Android内存不足,急!

seker_xinjian 2011-11-24 09:54:51
我的应用里面需要显示大量的图片,这些图片是需要从网络上下载的,因此我做了两级的图片Cache。第一级的缓存是当图片下载下来后,保存到sdcard(或者是internal storage)中,这样就不用重复的下载了。由于同一张图片在同一个Activity中可能被多个ImageView使用,所以我做了第二级缓存:内存缓存。我使用是的是HashMap<String, SoftReference<?>>,其中作为key的Strin是文件的名字。这样子可以避免同一个图片文件被重复的decode多次了。

在我的应用中,当需要使用图片时,首先从内存的ImageCache中查找,如果查找不到,就尝试从sdcard中加载,如果还是找不到再从网络上下载。

这种设计,我认为已经能够最大限度的节省内存,并保持运行效率的。然而即便如此,我的应用在运行中,依然会抛出内存不足的异常:

11-21 21:31:38.794: E/dalvikvm-heap(26024): 1363200-byte external allocation too large for this process.
11-21 21:31:38.794: E/dalvikvm(26024): Out of memory: Heap Size=17279KB, Allocated=4237KB, Bitmap Size=6119KB, Limit=21884KB
11-21 21:31:38.794: E/dalvikvm(26024): Trim info: Footprint=7687KB, Allowed Footprint=17799KB, Trimmed=520KB

因此,必须要继续优化我的代码。首先,第二级缓存(内存缓存)的实现,我参照的是一个开源的工程的代码:https://github.com/kaeppler/droid-fu 。他使用的是
HashMap<String, SoftReference<byte[]>>。当在内存中查询到想要的图片时,将byte[]重新decode成Bitmap,显示在UI上去。

我觉得这种做法没有起到真正的缓存的效果,因为byte[]占据一份内存,当多次decode成Bitmap后,内存中会存在多个相同的Bitmap。这样的结果是比不做内存缓存还多消耗了byte[]所占据那份内存。

而且,这些Bitmap最终又会被封装成BitmapDrawable,所以是不是将内存中的缓存做成HashMap<String, SoftReference<BitmapDrawable>>,是最佳的方案呢?

在内存中,byte[],Bitmap,BitmapDrawable这三者所占用的内存关系是怎么样的呢?

我该怎么样去管理Android的内存?
...全文
4061 35 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
reeceran 2012-07-24
  • 打赏
  • 举报
回复
缓存byte[]比缓存bitmap少用很多内存的,因为网络下载后的数据是byte[],你再转换成bitmap,会比原数据大很多。不过怎样存在你说的内存有2份相同的数据,但毕竟很少,就几张图片可能会存在2份。
clwwlc 2012-06-08
  • 打赏
  • 举报
回复
咱俩的问题基本一样,我采用的策略是把图片的url作为键值将得到的bitmap对象存入缓冲中,每次都是先从缓存拿,缓存没再从本地拿,本地没再从网络拿,只是我再每次加载新的图片时都去判断缓冲中bitmap的总大小有多大,当大于8m时根据键值获得掉最早加入的bitmap对象,将其释放掉,但这样释放的话,我可能给上一面已经加载过的图片对象也释放了,这样当我再回到上一页面就会出现用了一个已经释放内存的资源这样的错误,所以解决了内存溢出,又会出现trying to use a recycled bitmap。。。这样的异常
fengwenqi2010 2011-12-03
  • 打赏
  • 举报
回复
Andriod的内存问题是个硬伤,对于不能修改SDK的程序来说,要想完美解决OOM,我个人认为几乎是不可能的。如果能定制机器,还能修改VM的内存大小限制,可是多数的APP都是运行在标准的framework之上。
我最近的一个项目也是有很多关于OOM的问题,最后我还是基本解决。
先说下功能:
用户可以从SDCard中选择一个图片做界面的背景图片(图片可能是4000*3000,甚至更大),同时程序里面还有很多针对View截图的功能。所以我遇到的问题:
1,如果把SDCard中选择的图片显示为View的背景(View大小为600 * 1204,这样的View个数是2 - 12不等)。
2,对这些View截图时,可能会出OOM,也就是说drawing cache得不到。

我的解决办法是,
对于第一个问题:可以用动态计算BitmapFactory.Options,注意是动态计算,如果图片大,sample size就大,这个算法有点复杂,可以参考系统APP Gallery里面的代码。
第二个问题:根本不用去得drawing cache,因为它里面还是创建bitmap,所以,其实我中需要创建一张bitmap,然后把View画到这个bitmap上面,至于如何画,你们懂的。canvas.setbitmap(bitmap), view.draw(canvas).

同时说一下,要想不OOM,又要想显示效果好,我认为不可兼得,android与ios程序内存管理本来就不一样,所以说显示效果不同也正常。
seker_xinjian 2011-11-28
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 lww200888 的回复:]

写个函数对你的图片进行压缩。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import androi……
[/Quote]

要是这么干的话,那么这款软件就视觉效果而言,android玩家在iphone玩家面前恐怕就抬不起头了。。。
yytt4040 2011-11-28
  • 打赏
  • 举报
回复
不要用HashMap<String, SoftReference<byte[]>> ,就用HashMap<String, SoftReference<BitmapDrawable>>可。
另外注意内存缓存的大小,不能太大。
可用adb shell dumpsys meminfo 查看内存占用情况,优化内存占用。
passself 2011-11-28
  • 打赏
  • 举报
回复
内存其实是不可以省的,只是因为你有时可以暂时不 用就暂时清空,于是你也只能采取这个办法,或者说你可以把图片换成jpg的比较小
lww200888 2011-11-28
  • 打赏
  • 举报
回复
写个函数对你的图片进行压缩。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.BitmapFactory.Options;

/**
* 加载大图片工具类:解决android加载大图片时报OOM异常 解决原理:先设置缩放选项,再读取缩放的图片数据到内存,规避了内存引起的OOM
*
* @author: liweiwei
*
* @time:2011/10/17
*/
public class BitmapUtil {

public static final int UNCONSTRAINED = -1;

/*
* 获得设置信息
*/
public static Options getOptions(String path) {
Options options = new Options();
options.inJustDecodeBounds = true;// 只描边,不读取数据
BitmapFactory.decodeFile(path, options);
return options;
}

/**
* 获得图像
*
* @param path
* @param options
* @return
* @throws FileNotFoundException
*/
public static Bitmap getBitmapByPath(String path, Options options,
int screenWidth, int screenHeight) throws FileNotFoundException {
File file = new File(path);
if (!file.exists()) {
throw new FileNotFoundException();
}
FileInputStream in = null;
in = new FileInputStream(file);
if (options != null) {
Rect r = getScreenRegion(screenWidth, screenHeight);
int w = r.width();
int h = r.height();
int maxSize = w > h ? w : h;
int inSimpleSize = computeSampleSize(options, maxSize, w * h);
options.inSampleSize = inSimpleSize; // 设置缩放比例
options.inJustDecodeBounds = false;
}
Bitmap b = BitmapFactory.decodeStream(in, null, options);
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return b;
}

private static Rect getScreenRegion(int width, int height) {
return new Rect(0, 0, width, height);
}

/**
* 获取需要进行缩放的比例,即options.inSampleSize
*
* @param options
* @param minSideLength
* @param maxNumOfPixels
* @return
*/
public static int computeSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength,
maxNumOfPixels);

int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}

return roundedSize;
}

private static int computeInitialSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;

int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : (int) Math
.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == UNCONSTRAINED) ? 128 : (int) Math
.min(Math.floor(w / minSideLength), Math.floor(h
/ minSideLength));

if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}

if ((maxNumOfPixels == UNCONSTRAINED)
&& (minSideLength == UNCONSTRAINED)) {
return 1;
} else if (minSideLength == UNCONSTRAINED) {
return lowerBound;
} else {
return upperBound;
}
}
}
forever_crying 2011-11-28
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 yiyaaixuexi 的回复:]

每次看到内存溢出的帖子,我都忍不住高亮一下,内存管理,是永恒的话题。
不知道你有没有耐心看完:http://7dot9.com/2010/08/android-bitmap%E5%86%85%E5%AD%98%E9%99%90%E5%88%B6/
[/Quote]

永恒的解决不了的话题
wensefu 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 seker_xinjian 的回复:]

引用 7 楼 wensefu 的回复:

Bitmap应该要及时回收。


倒是说说怎么回收啊?我的内存Cache中的Bitmap都是SoftReference,系统是可以回收它们的。连GC我都调用了。
[/Quote]
回收就是调recycle()了
在多线程环境下要留意 不要出现trying to use a recycled bitmap。。。那种错就好了
seker_xinjian 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 jdpxiaoming 的回复:]

引用 13 楼 ilittleone 的回复:

引用 11 楼 pochuanpiao 的回复:
呵呵,跟着学习了


引用 9 楼 seker_xinjian 的回复:

引用 7 楼 wensefu 的回复:

Bitmap应该要及时回收。


倒是说说怎么回收啊?我的内存Cache中的Bitmap都是SoftReference,系统是可以回收它们的。连GC我都调……
[/Quote]

view缓存意识 是 你在 getView(){}的时候 最好 不要每次 都 new 先 判断 contentView是否存在。

我的程序中全部都是判断contentView是否存在,如果存在都是重复使用的,大家可以放心这点。
jdpxiaoming 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 ilittleone 的回复:]

引用 11 楼 pochuanpiao 的回复:
呵呵,跟着学习了


引用 9 楼 seker_xinjian 的回复:

引用 7 楼 wensefu 的回复:

Bitmap应该要及时回收。


倒是说说怎么回收啊?我的内存Cache中的Bitmap都是SoftReference,系统是可以回收它们的。连GC我都调用了。


这个不是gc所能搞定的,你知道gc在……
[/Quote]

朋友 ,我觉的 你对这个研究的很好
这位朋友的回复
楼主多 考虑 下 ,还有 直接 用 bitmap来 软引用吧
因为 每次 转化成 Btimap的时候 这个工厂方法 是有自己的 内存容量的 我测试过 6.4M 多 而且 耗时 。
view缓存意识 是 你在 getView(){}的时候 最好 不要每次 都 new 先 判断 contentView是否存在。
xchchx 2011-11-25
  • 打赏
  • 举报
回复
OOM确实是不可避免的,只能尽量取减少它的出现。因为程序在运行过程中会产生内存碎片,而我们的内存大小是固定的,程序运行时间越长,进行各种读取释放加载数据运算越多,那么产生的碎片越多。假如现在剩余内存有1M,我要加载一个500k的图片,理论上不应该OOM,但如果这1M内存被其他数据分割成了一小块一小块,最大的一块连续内存也只有400k,那么必然会出现OOM。所以我们要尽量减少数据读取释放次数,这样操作多了,就会产生更多的内存碎片,当加载占用连续大内存的资源时就可能会OOM
seker_xinjian 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 lingang1359 的回复:]

引用 19 楼 seker_xinjian 的回复:

引用 18 楼 lingang1359 的回复:

看到这个我忍不住要说一下了,我6-11月做的2个项目里面全部都涉及到大图片解析和大量图片加载……我表示很蛋疼。
最后,关于大图片解析,采取的是WeakReference和SoftReference都用,关于弱引用和软引用的区别,有需要的可以自己去查和学习一下。
大量图片加载当时……
[/Quote]

你的意思是gallery/listview滑动的时候不加载,等停住的时候加载?
lingang1359 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 seker_xinjian 的回复:]

引用 18 楼 lingang1359 的回复:

看到这个我忍不住要说一下了,我6-11月做的2个项目里面全部都涉及到大图片解析和大量图片加载……我表示很蛋疼。
最后,关于大图片解析,采取的是WeakReference和SoftReference都用,关于弱引用和软引用的区别,有需要的可以自己去查和学习一下。
大量图片加载当时做的是一个类似google market的项目,UI设计太复……
[/Quote]

能的,自己继承BaseAdapter,在对Gallery进行数据绑定的时候进行滑动加载处理
seker_xinjian 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 lingang1359 的回复:]

看到这个我忍不住要说一下了,我6-11月做的2个项目里面全部都涉及到大图片解析和大量图片加载……我表示很蛋疼。
最后,关于大图片解析,采取的是WeakReference和SoftReference都用,关于弱引用和软引用的区别,有需要的可以自己去查和学习一下。
大量图片加载当时做的是一个类似google market的项目,UI设计太复杂了,界面上面各种需要加载的图片,后来的解决办法是图片加……
[/Quote]

ListView的滑动加载,这个貌似很管用吧?
不过这边用的是自己写的一个控件,继承自Gallery,有那种伪3D的效果。能用上滑动加载不?
lingang1359 2011-11-25
  • 打赏
  • 举报
回复
看到这个我忍不住要说一下了,我6-11月做的2个项目里面全部都涉及到大图片解析和大量图片加载……我表示很蛋疼。
最后,关于大图片解析,采取的是WeakReference和SoftReference都用,关于弱引用和软引用的区别,有需要的可以自己去查和学习一下。
大量图片加载当时做的是一个类似google market的项目,UI设计太复杂了,界面上面各种需要加载的图片,后来的解决办法是图片加载时给loading进度条,不过不是整个UI线程阻塞,而是图片没有下载完的地方有Loading,其他地方依然是可以操作的,结合ListView的滑动加载,每次加载一屏的图片,感觉算是勉强解决了这些问题。
seker_xinjian 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 ilittleone 的回复:]

引用 11 楼 pochuanpiao 的回复:
呵呵,跟着学习了


引用 9 楼 seker_xinjian 的回复:

引用 7 楼 wensefu 的回复:

Bitmap应该要及时回收。


倒是说说怎么回收啊?我的内存Cache中的Bitmap都是SoftReference,系统是可以回收它们的。连GC我都调用了。


这个不是gc所能搞定的,你知道gc在……
[/Quote]

貌似行不通,我这个应用是社交类的,带有交互游戏性质。这些图片是游戏精灵的图片,一屏幕下有N多的,不能采取loading图片的。
seker_xinjian 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 qjsjp 的回复:]

从上面你写的来看,不管你怎么缓存,你是不是最终都要把从网络上下载的图直接加载到内存,那么如果图片过大就会内存溢出吧!你可以去试试这个东西,BitmapFactory.options,他的作用可以使你加载过大的图片不会造成内存溢出,他可以使你不用把图片加载到内存,就能得到图片的信息,你可以先等比例缩放图片,然后再加载到内存。
还有,我感觉你的第二级缓存不能说节省内存吧,能够提高效率,不知道是不是……
[/Quote]

BitmapFactory.options这个我不能用,因为这个软件有iphone版本的,iphone显示的出来的很高清,运行很流畅,你懂的...

我的第二级缓存主要目的有两个:一个是为了提高效率;还有个一原因就是同一屏幕中,很有可能同一张图片被显示多次,我做二级缓存就是避免同一张图片被decode N次。
seker_xinjian 2011-11-25
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 ilittleone 的回复:]

引用 11 楼 pochuanpiao 的回复:
呵呵,跟着学习了


引用 9 楼 seker_xinjian 的回复:

引用 7 楼 wensefu 的回复:

Bitmap应该要及时回收。


倒是说说怎么回收啊?我的内存Cache中的Bitmap都是SoftReference,系统是可以回收它们的。连GC我都调用了。


这个不是gc所能搞定的,你知道gc在……
[/Quote]

我用的SDK是2.1
冰天狼 2011-11-24
  • 打赏
  • 举报
回复
嗯,有内容的帖子,顶下,希望楼主早日解决。
加载更多回复(13)

80,472

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 Android
androidandroid-studioandroidx 技术论坛(原bbs)
社区管理员
  • Android
  • yechaoa
  • 失落夏天
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧