android系统开发怎样捕捉系统异常,比如内存溢出等系统异...

有效解决Android加载大图片时内存溢出的问题 - 80后Programer---&zhiweiofli - ITeye技术网站
博客分类:
首先解析一下基本的知识:
位图模式,bitmap颜色位数是1位
灰度模式,bitmap颜色位数是8位,和256色一样
RGB模式,bitmap颜色位数是24位 在RGB模式下,一个像素对应的是红、绿、蓝三个字节
CMYK模式,bitmap颜色位数是32位
在CMYK模式下,一个像素对应的是青、品、黄、黑四个字节
图像文件的字节数(Byte) = 图像分辨率*颜色深度/8(bit/8)
例如:一幅640*480图像分辨率、RGB色一般为24位真彩色,图像未经压缩的数据容量为:640X480X24/8=921600字节=900KB(1KB=l千字节=1024字节)。注:一个图像文件占的磁盘空间大小还和磁盘的文件格式有关。如:NTFS最小单位为4KB 所以图像文件大小肯定是4KB的倍数。但是有图图片压缩算法的存在,图片文件在保存时,体积要比在内存的大小小得多,如640x480的图片文件大小一般只在200K~300K。这也是为什么,加载几MB的图片文件,会导致JVM内存溢出,导致OutofMemoryException的原因。
由上面的公式,我们可以得出,加载的图片所占的内存大小,取决于其分辨率和颜色数。
我们再来看看Google官方的介绍:
这个已经非常的明白了,我们VM的app进程所获得的内存只有区区的16MB,普普通通的5MP摄像头拍出来的图片,直接加载,将占用接近19MB的内存。可见,不进行压缩,内存将会直接溢出。
再了解一下,android读取解析图片的方式,基本与Java的方式类型,通过文件输入流,然后进行解码,再转成图片格式;
当然google的android也为我们封装好了若干方法,来方便快捷地完成这项工作,如ImageView的setImageBitmap,setImageResource,BitmapFactory的decodeResource等,但是尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存;
因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的source,加载显示。decodeStream最大的秘密在于其直接调用JNI&&nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。
在使用decodeStream读取图片时,再加上Config参数,就可以更有效地控制加载目标的内存大小,从而更有效阻止抛OutofMemoryException异常,下面用一段代码说明:
public static Bitmap readBitmapAutoSize(String filePath, int outWidth, int outHeight) {
//outWidth和outHeight是目标图片的最大宽度和高度,用作限制
FileInputStream fs =
BufferedInputStream bs =
fs = new FileInputStream(filePath);
bs = new BufferedInputStream(fs);
BitmapFactory.Options options = setBitmapOption(filePath, outWidth, outHeight);
return BitmapFactory.decodeStream(bs, null, options);
} catch (Exception e) {
e.printStackTrace();
} finally {
bs.close();
fs.close();
} catch (Exception e) {
e.printStackTrace();
private static BitmapFactory.Options setBitmapOption(String file, int width, int height) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds =
//设置只是解码图片的边距,此操作目的是度量图片的实际宽度和高度
BitmapFactory.decodeFile(file, opt);
int outWidth = opt.outW //获得图片的实际高和宽
int outHeight = opt.outH
opt.inDither =
opt.inPreferredConfig = Bitmap.Config.RGB_565;
//设置加载图片的颜色数为16bit,默认是RGB_8888,表示24bit颜色和透明通道,但一般用不上
opt.inSampleSize = 1;
//设置缩放比,1表示原比例,2表示原来的四分之一....
//计算缩放比
if (outWidth != 0 && outHeight != 0 && width != 0 && height != 0) {
int sampleSize = (outWidth / width + outHeight / height) / 2;
opt.inSampleSize = sampleS
opt.inJustDecodeBounds =//最后把标志复原
另外,decodeStream直接拿的图片来读取字节码了, 不会根据机器的各种分辨率来自动适应, 使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源, 否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。 可参考下面的代码:
BitmapFactory.Options opts = new BitmapFactory.Options();
//设置图片的DPI为当前手机的屏幕dpi
opts.inTargetDensity = ctx.getResources().getDisplayMetrics().densityD
opts.inScaled =
另外,图片的bitmap对象为大对象,不用了要注意主动回收,
if(!bmp.isRecycle() ){
bmp.recycle()
//回收图片所占的内存
system.gc()
//提醒系统及时回收
浏览 16675
zhiweiofli
浏览: 177186 次
来自: 广州
谢谢,解决了我大问题
lhy046510 写道lz可以给个列子吗?谢谢
[img][list][*]引用[u][i][b][/b][/ ...
芦茨法 写道我也有碰到这个问题,我的解决方案是在父容器Acti ...
我也有碰到这个问题,我的解决方案是在父容器Activity与了 ...1986人阅读
最近在做一款塔防游戏,用的事surfaceview框架,由于图片过多,而且游戏过程中都需要这些图片,所以加载成bitmap后造成OOM(out of memory)异常。下面是我一步一步找解决此问题的纪录,再此分享,希望对以后出现此问题的开发者有所帮助。
第一:出现问题,我的测试手机是2。2android操作系统,不会出现oom问题,但是在老板的android4.2上却出现了问题,因为是oom,所以我首先想到的是手动改变手机的内存大小限制。网上有些帖子说可以通过函数设置应用的HEAP SIZE来解决这个问题,其实是不对的。 VMRuntime.getRuntime().setMinimumHeapSize(NewSize); 堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。Max
Heap Size,是堆内存的上限值,Android的缺省值是16M(某些机型是24M),对于普通应用这是不能改的。函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值时仍然采用堆的上限值,对于内存不足没什么作用。 setTargetHeapUtilization(float newTarget) 可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。在手机上进行了多次测试,确实不好使,在此,我断了改变内存限制的方法。
第二:查找出现问题的原因。1,在网上搜索bitmap内存溢出,找到很多说是因为图片大小引起的此问题。观察我的资源文件,没有太大的图片,只是图片数量过多,有将近900张,分别找出一张最大的图片和几张比较大的图片,单独测试,没有发现问题。方法1排除。
2,既然图片数量过多,突破点可能就是图片数量问题。于是分别找了200,400,600图片进行测试,在500左右的时候遇到错误,通过宝哥知道了将小图片整合存放到一张大图的方法,以此来减少图片的数量,但是仔细想想,加载成bitmap的时候还是要切割成小图生成bitmap,所以对此方法表示怀疑。由于以前没用过此方法,试试也无妨。所用到的工具是gdx—texturepackger,它只是一个工具,这里就不多说了。测试的最终结果是还是oom。方法2排除。
3,现在看来,既然不是图片数量的问题,而且会在500张左右的时候报错,那就可能是占用内存大小的问题了,Android手机有内存限制,但是我的图片大小又大于这个限制,这让我头疼了很长时间,研究国外的一些文章,从中发现了一些有用的信息,这些信息能够加深你对Android的解析bitmap机制的理解,在此分享:
As of Honeycomb Bitmap data is allocated in the VM heap.
作为蜂窝位图数据是在VM分配堆。)
There is a reference to it in the VM heap (which is small), but the actual data is allocated in the Native heap by the underlying Skia graphics library. 有一个引用在VM堆(小),但实际的数据是在本机堆分配由底层Skia图形库。
Unfortunately, while the definition of BitmapFactory.decode…() says that it returns null if the image data could not be decoded, the Skia implementation (or rather the JNI glue between the Java code and Skia) logs the message you’re seeing (“VM won’t let us
allocate xxxx bytes”) and then throws an OutOfMemory exception with the misleading message “bitmap size exceeds VM budget”. 不幸的是,虽然BitmapFactory.decode的定义…()表示,它返回null如果图像数据不能解码,Skia实现(或者说JNI胶之间的Java代码和Skia)日志消息你看到(“VM不会让我们分配xxxx字节”),然后抛出一个OutOfMemory异常与误导信息”位图的大小超过VM预算”。
The issue is not in the VM heap but is rather in the Native heap. 这个问题不是在VM堆而是在本机堆。
The Nat?ve heap is shared between running applications, so the amount of free space depends on what other applications are running and their bitmap usage. 本机堆是正在运行的应用程序之间共享,因此空闲空间的大小取决于其他运行程序,他们使用的位图。
However, I have found that getNativeHeapFreeSize() and getNativeHeapSize() are not reliable. 然而,我发现getNativeHeapFreeSize()和getNativeHeapSize()是不可靠的。
The Native heap size varies by platform. 本机堆大小不同的平台。
So at startup, we check the maximum allowed VM heap size to determine the maximum allowed Native heap size. 所以在启动时,我们检查最大允许VM堆大小来确定最大允许本机堆大小。
“Bitmap data is not allocated in the VM heap” — it is allocated on the VM heap as of Honeycomb “位图数据不是在VM分配堆”——这是VM分配的堆在蜂窝Yes. 是的。 As of Honeycomb (v3.0), bitmap data is allocated on the VM heap. 作为蜂窝(v3.0),位图数据堆上分配VM。 So all of the above only applies
to Gingerbread (v2.3.x) and before 所以所有上述只适用于姜饼(v2 3 x)和之前
这些信息零零散散,但是不难发现,问题的原因就在于根据Android版本的不同,bitmap data存放的位置是不同的,3.0以前是分配在native heap上,3.0以后是分配在VM heap上。
为了验证这个问题,我们需要抓去heap快照,众所周知,eclipse中的ddms可以查看heap信息,但是不够全面,这里我用到了adb shell dumpsys meminfo+包名 这条命令来查看heap信息,对比两个机子的不同如下:
<span style="color:#.0
从中不难发现,bitmap的存放位置根据Android版本的不同真的有所不同。好了,下面就是找出怎么把图片存放到native heap里就行了,BitmapFactory里就那么几个decode方法,很容易找到BitmapFactory .decodeStream就可以解决。下面贴一下代码:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.ARGB_8888;
options.inPurgeable =// 允许可清除
options.inInputShareable =// 以上options的两个属性必须联合使用才会有效果
String sname = String.format( “xxx.png”, sTowerStyle, j, sDirction, i);
InputStream is = am.open(sname);
arrBmp[ iBmpIndex] = BitmapFactory .decodeStream(is, null, options);
ok搞定手工。
小问题大发现:1.遇到问题,不要急躁。最初遇到这个问题的时候以为很好解决,试了几种方法后还是解决不了,内心难免会有挫败感,这个时候,最需要的是耐心。
<span style="color:#.网上有很多资源,但是能不能查得到就是自己的问题了,我发现那些编程老手们查找问题总是能够准确定位,快速的找到解决方法。以后要加强这方面的锻炼。
<span style="color:#.国内的资源大多偏向解决问题,国外的资源大多偏向分析问题,所以有的时候还是需要多看看外文的一些资料。当然这需要不错的英文功底。当初看外文的资料,头都大了。这是需要加强的一个方面。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:34727次
排名:千里之外
原创:29篇
转载:12篇
(2)(10)(6)(1)(1)(1)(1)(2)(1)(1)(1)(3)(4)(5)(2)Android遇到内存溢出(Out Of Memory)BUG的经验与解决办法_java中参数的传递方式,该如何处理_[1000分]哪位高手给个Linux下可用的 pthread_suspend/resume 来, 不想到libgc里慢慢去找了__脚本百事通
稍等,加载中……
^_^请注意,有可能下面的2篇文章才是您想要的内容:
Android遇到内存溢出(Out Of Memory)BUG的经验与解决办法
java中参数的传递方式,该如何处理
[1000分]哪位高手给个Linux下可用的 pthread_suspend/resume 来, 不想到libgc里慢慢去找了
Android遇到内存溢出(Out Of Memory)BUG的经验与解决办法
Android遇到内存溢出(Out Of Memory)BUG的经验与解决方法
经过信息检索,我弄清了这样一个事实:Android虚拟机不允许单个程序中的Bitmap占用超过8M的内存,一旦超过了就会报错,而报的错正是bitmap size exceeds VM budget.现在好了,这一切看似如此简单:要想程序的bitmap小于8M,要么就在用了bitmap后立即回收这部分内存,要么就压缩图片的大小啊。第一种方法--及时回收bitmap内存:一般而言,回收bitmap内存可以用到以下代码1.if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
5.System.gc();
bitmap.recycle()方法用于回收该bitmap所占用的内存,接着将bitmap置空,最后,别忘了用System.gc()调用一下系统的垃圾回收器。在这里要声明一下,bitmap可以有多个(以为着可以有多个if语句),但System.gc()最好只有一个(所以我将它写在了if语句外),因为System.gc()每次调用都要将整个内存扫描一遍,因而如果多次调用的话会影响程序运行的速度。为了程序的效率,我将它放在了所有回收语句之后,这样已经起到了它的效果,还节约的时间。回收bitmap已经知道了,那么“及时”怎么理解呢?根据我的实际经验,bitmap发挥作用的地方要么在View里,要么在Activity里(当然肯定有其他区域,但是原理都是类似的),回收bitmap的地方最好写在这些区域刚刚不使用bitmap了的时刻。比如说View如果使用了bitmap,就应该在这个View不再绘制了的时候回收,或者是在跳转到的下一个区域的代码中回收;再比如说SurfaceView,就应该在onSurfaceDestroyed这个方法中回收;同理,如果Activity使用了bitmap,就可以在onStop或者onDestroy方法中回收......结合以上的共同点,“及时回收”的原理就是在使用了bitmap的区域结束时或结束后回收。第二种方法--压缩图片:这个方法当然很简单了,就是使图片体积大小变小,可以有两种方式:一种是使图片质量降低(分辨率不变),另一种是使图片分辨率降低(分辨率改变)。总之,使图片大小变小就行了。实践证明,使图片质量降低(分辨率不变)可以大幅度地减小体积,而且质量的差异肉眼看上去并不明显。我刚开始使用的就是这两种方法,原理很简单,可是,我的BUG发生虽然没那么频繁了,但是它依然存在!!后来经过几天的努力与尝试,结合我项目的一些具体情况,我终于解决了这个令人头痛的BUG,但是事实却有点出乎我的意料。当我使用了上述两种方法BUG依然还没解决的时候,我开始怀疑,bitmap超过8M会报错,可现在我把前前后后的bitmap都回收了,不可能还有8M了,那为什么还会报错呢?终于我发现了这个原因:当内存中已经被一些bitmap使用过之后,无论被回收与否,它都会变得特别“敏感”,这个时候,如果bitmap突然要占用大量的内存,即使和之前已经剩下的内存加起来不到8M,系统也会报错,原因是它变“敏感”了!我不知道这个用底层原理如何解释比较好,但是我想“敏感”这个词应该可以很形象地进行解释。于是,为了顺应内存的“敏感性”,我将那个需要同时装载多个大体积bitmap的地方进行了修改,用到了以下方法:1.//压缩,用于节省BITMAP内存空间--解决BUG的关键步骤
2. BitmapFactory.Options opts = new BitmapFactory.Options();
3.opts.inSampleSize = 2;
//这个的值压缩的倍数(2的整数倍),数值越小,压缩率越小,图片越清晰
5.//返回原图解码之后的bitmap对象
6. bitmap = BitmapFactory.decodeResource(Context, ResourcesId, opts);
即先将图片缩小一倍,再将这缩小了一倍的图片作为bitmap存入内存,这样一来,它占用的bitmap内存大大减小。后来经测试,BUG果然解决了。图片缩小一倍后,顺应了内存的“敏感性”,也就不会再报错了。 以上方法应该足以解决大多数bitmap内存溢出问题,但是具体情况还是要具体分析。在Android中:
1.一个进程的内存可以由2个部分组成:native和dalvik,,dalvik就是我们平常说的java堆,我们创建的对象是在这里面分配的,而bitmap是直接在native上分配的,对于内存的限制是 native+dalvik 不能超过最大限制。
2.更加奇怪的是这个:一旦内存分配给Java后,以后这块内存即使释放后,也只能给Java的使用,这个估计跟java虚拟机里把内存分成好几块进行缓存的原因有关,反正C就别想用到这块的内存了,所以如果Java突然占用了一个大块内存,即使很快释放了:
C能使用的内存 = 16M - Java某一瞬间占用的最大内存。
而Bitmap的生成是通过malloc进行内存分配的,占用的是C的内存,这个也就说明了,上述的4MBitmap无法生成的原因,因为在13M被Java用过后,剩下C能用的只有3M了。
java中参数的传递方式,该如何处理
java中参数的传递方式分两种,一种是值传递,一种是引用传递。
值传递,传递的是一个拷贝,就好像给你一带大米。引用传递,传递一个地址,就好像给你一张纸,纸上写着大米在厨房,你自己拿。
我虽然能理解,但是我还是初学,想请教大家写两个具体的方法来比较一下,欢迎大家讨论------解决方案--------------------没什么可讨论的,在JAVA里你不可能改变参数传递方式
原始类型都是值传递,对象类型永远是引用传递
------解决方案--------------------知道参数传递在对象和int上的效果就可以了,多讨论也没什么意义。 ------解决方案--------------------java 没什么说的 多数是引用。值传递,是传输具体的数,或字符没什么价值。
------解决方案--------------------java中采用的都是值传递
就是说,即便参数是对象的引用,方法得到的也是引用的copy
&java2核心技术 卷1&
4.5章节 ------解决方案--------------------所有传递传的都是变量的值,都是值传递````只是```原始类型变量持有值本身,对象变量持有对象引用````应该理解对象变量和原始类型变量的区别``
------解决方案--------------------java中采用的都是值传递
就是说,即便参数是对象的引用,方法得到的也是引用的copy
&java2核心技术 卷1&
===============================
这位兄弟说的对,在JAVA中只有值传递,不像C,C++中有值和引用(地址)传递.
比如在一个方法中,传了一个对象过去,即以对象作为参数,然后在方法中修改
对象的某个值,然后返回这个值,会发现该对象的该属性值并没有改变,还是原来
的值,所以说明这是值传递,不像C++中,在C++中这时属性值就会改变,这也是JAVA
与C++不一样的地方.
------解决方案--------------------java中采用的都是值传递
就是说,即便参数是对象的引用,方法得到的也是引用的copy
这说的太对了!我支持
------解决方案--------------------顶 9楼------解决方案--------------------都可以理解是值传递,就是copy你需要理解的是对象类型为引用类型,仅此而已。
------解决方案--------------------呵呵 这个问题老生常谈了JAVA是不支持引用传递的所谓的引用传递也是 引用的副本你只要注意这点就行------解决方案--------------------java中只有一种传递方式,就是传址。其实任何语言参数传递也都是传址方式的。传值是这样的:将原来的数据在内存中形成一个拷贝,然后在传递给你这个拷贝的地址。所以函数传递参数只有一种方式,那就是传址。据说:传值方式只是国内的高校老师为了讲课方便介绍的概念。在国外已经抛弃了这个概念。统一的就是传址。
------解决方案--------------------关键是看能不能传过去
[1000分]哪位高手给个Linux下可用的 pthread_suspend/resume 来, 不想到libgc里慢慢去找了
[1000分]谁给个Linux下可用的 pthread_suspend/resume 来, 不想到libgc里慢慢去找了这300分是给顶帖子的人的. 谁给个可以用的我单独开帖子给分, 要可用分也可以...
无聊的管理内存啥的把人弄烦了,开始准备用libgc的, 后来看了看发现不是很适合嵌入式环境, 打算还是自己写个GC的库算了, 搞的也基本差不多了, 单线程的在 WIN32/WIN64/Linux/X64 Linux/ARM Linux/Android下测试都没有问题...
多线程的因为 WIN下有现成的 Suspend/ResumeThread, 跑起来也没有问题了, 就是在 Linux/Android 下遇到了点麻烦, 现在我往线程发 SIGUSR2, 在 signal handle 里 pthread_mutex_lock/pthread_cond_wait 等待resume的事件, 这个会有一定的概率(1/1000左右, 基本都出现在线程开始启动/结束的时候, 我在线程开始结束时加上sleep会大大降低出错的概率, 基本上 sleep 200ms 就基本不会出错了, 但这看起来不是个好办法)线程没有被挂起, 因为 mutex_lock/cond_wait/sem_wait 这些函数都不是 signal safe 的, 用 sem_wait 也偶尔会deadlock ..
看来要去libgc里慢慢抄了...
------解决方案--------------------友情帮顶 字数.cpp------解决方案--------------------友情帮顶。
只用纯消息驱动来使用线程的人飘过。------解决方案--------------------绑顶,
表示也是被动接收消息,再开启对应的服务线程啦!------解决方案--------------------凑个热闹..------解决方案--------------------大眼瞪小眼。------解决方案--------------------------解决方案--------------------------解决方案--------------------------解决方案--------------------貌似很厉害额------解决方案--------------------能懂楼主 讲 什么了。。。 不过还是经验不够,想不到用途------解决方案--------------------看来楼主还得自己找
睡觉------解决方案--------------------
Quote: 引用:
能懂楼主 讲 什么了。。。 不过还是经验不够,想不到用途
写了用途啊, 做GC啊, GC的时候必须让全世界都暂停, 这时候就会ANR.
看来楼主还得自己找
已经写好了, WIN32/WIN64/Linux/Linux X64/Android 下都跑了差不多一天多的单元测试, 应该没有问题了 ...
如果您想提高自己的技术水平,欢迎加入本站官方1号QQ群:&&,&&2号QQ群:,在群里结识技术精英和交流技术^_^
本站联系邮箱:Android&内存溢出解决方案(OOM)&整理总结
(本文对我帮助很大,在此谢谢原作者)
原创作品,允许转载,转载时请务必以超链接形式标明文章
、作者信息和本声明。否则将追究法律责任。
在最近做的工程中发现加载的图片太多或图片过大时经常出现OOM问题,找网上资料也提供了很多方法,但自己感觉有点乱,特此,今天在不同型号的三款安卓手机上做了测试,因为有效果也有结果,今天小马就做个详细的总结,以供朋友们共同交流学习,也供自己以后在解决OOM问题上有所提高,提前讲下,片幅有点长,涉及的东西太多,大家耐心看,肯定有收获的,里面的很多东西小马也是学习参考网络资料使用的,先来简单讲下下:
一般我们大家在遇到内存问题的时候常用的方式网上也有相关资料,大体如下几种:
一:在内存引用上做些处理,常用的有软引用、强化引用、弱引用
二:在内存中加载图片时直接在内存中做处理,如:边界压缩
三:动态回收内存
四:优化Dalvik虚拟机的堆内存分配
五:自定义堆内存大小
可是真的有这么简单吗,就用以上方式就能解决OOM了?不是的,继续来看...
下面小马就照着上面的次序来整理下解决的几种方式,数字序号与上面对应:
1:软引用(SoftReference)、虚引用(PhantomRefrence)、弱引用(WeakReference),这三个类是对heap中java对象的应用,通过这个三个类可以和gc做简单的交互,除了这三个以外还有一个是最常用的强引用
1.1:强引用,例如下面代码:
Object o1=o;
上面代码中第一句是在heap堆中创建新的Object对象通过o引用这个对象,第二句是通过o建立o1到new
Object()这个heap堆中的对象的引用,这两个引用都是强引用.只要存在对heap中对象的引用,gc就不会收集该对象.如果通过如下代码:
heap中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。对于对象是属于哪种可及的对象,由他的最强的引用决定。如下:
String("abc"); //1
SoftReference&String&
abcSoftRef=new
SoftReference&String&(abc);
WeakReference&String&
abcWeakRef = new
WeakReference&String&(abc);
abc=null; //4
abcSoftRef.clear();//5
上面的代码中:
第一行在heap对中创建内容为“abc”的对象,并建立abc到该对象的强引用,该对象是强可及的。第二行和第三行分别建立对heap中对象的软引用和弱引用,此时heap中的对象仍是强可及的。第四行之后heap中对象不再是强可及的,变成软可及的。同样第五行执行之后变成弱可及的。
1.2:软引用
软引用是主要用于内存敏感的高速缓存。在jvm报告内存不足之前会清除所有的软引用,这样以来gc就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc的算法和gc运行时可用内存的大小。当gc决定要收集软引用是执行以下过程,以上面的abcSoftRef为例:
首先将abcSoftRef的referent设置为null,不再引用heap中的new
String("abc")对象。
2 将heap中的new
String("abc")对象设置为可结束的(finalizable)。
3 当heap中的new
String("abc")对象的finalize()方法被运行而且该对象占用的内存被释放,
abcSoftRef被添加到它的ReferenceQueue中。
注:对ReferenceQueue软引用和弱引用可以有可无,但是虚引用必须有,参见:
Reference(T
paramT, ReferenceQueue&? super
T&paramReferenceQueue)
被 Soft Reference 指到的对象,即使没有任何 Direct Reference,也不会被清除。一直要到 JVM
内存不足且 没有 Direct Reference 时才会清除,SoftReference 是用来设计 object-cache
之用的。如此一来 SoftReference 不但可以把对象 cache 起来,也不会造成内存不足的错误
(OutOfMemoryError)。我觉得 Soft Reference 也适合拿来实作 pooling
Refenrence sr = new
SoftReference(obj);
if(sr!=null){
obj = new A();
SoftReference(obj);
1.3:弱引用
当gc碰到弱可及对象,并释放abcWeakRef的引用,收集该对象。但是gc可能需要对此运用才能找到该弱可及对象。通过如下代码可以了明了的看出它的作用:
String("abc");
WeakReference&String&
abcWeakRef = new
WeakReference&String&(abc);
System.out.println("before gc:
"+abcWeakRef.get());
System.gc();
System.out.println("after gc:
"+abcWeakRef.get());
before gc: abc
after gc: null
gc收集弱可及对象的执行过程和软可及一样,只是gc不会根据内存情况来决定是不是收集该对象。如果你希望能随时取得某对象的信息,但又不想影响此对象的垃圾收集,那么你应该用
Weak Reference 来记住此对象,而不是用一般的 reference。
WeakReference wr =
WeakReference(obj);
obj = null;
//等待一段时间,obj对象就会被垃圾回收
  if (wr.get()==null) {
  System.out.println("obj 已经被清除了
  } else {
  System.out.println("obj
尚未被清除,其信息是
"+obj.toString());
在此例中,透过 get() 可以取得此 Reference
的所指到的对象,如果返回值为 null 的话,代表此对象已经被清除。这类的技巧,在设计 Optimizer 或 Debugger
这类的程序时常会用到,因为这类程序需要取得某对象的信息,但是不可以 影响此对象的垃圾收集。
1.4:虚引用
就是没有的意思,建立虚引用之后通过get方法返回结果始终为null,通过源代码你会发现,虚引用通向会把引用的对象写进referent,只是get方法返回结果为null.先看一下和gc交互的过程在说一下他的作用.
1.4.1 不把referent设置为null, 直接把heap中的new
String("abc")对象设置为可结束的(finalizable).
1.4.2 与软引用和弱引用不同,
先把PhantomRefrence对象添加到它的ReferenceQueue中.然后在释放虚可及的对象.
你会发现在收集heap中的new
String("abc")对象之前,你就可以做一些其他的事情.通过以下代码可以了解他的作用.
java.lang.ref.PhantomR
java.lang.ref.R
java.lang.ref.ReferenceQ
java.lang.reflect.F
public class
public static
boolean isRun =
public static
void main(String[] args)
throws Exception
String abc = new String("abc");
System.out.println(abc.getClass() + "@" +
abc.hashCode());
final ReferenceQueue referenceQueue =
ReferenceQueue&String&();
new Thread() {
public void
while (isRun) {
Object o =
referenceQueue.poll();
if (o != null) {
Field rereferent =
Reference.class
.getDeclaredField("referent");
rereferent.setAccessible(true);
Object result =
rereferent.get(o);
System.out.println("gc will
+ result.getClass() +
result.hashCode());
} catch (Exception e)
e.printStackTrace();
}.start();
PhantomReference&String&
abcWeakRef = new
PhantomReference&String&(abc,
referenceQueue);
abc = null;
Thread.currentThread().sleep(3000);
System.gc();
Thread.currentThread().sleep(3000);
isRun = false;
class java.lang.String@96354
gc will collect:class java.lang.String@96354 好了,关于引用就讲到这,下面看2
2:在内存中压缩小马做了下测试,对于少量不太大的图片这种方式可行,但太多而又大的图片小马用个笨的方式就是,先在内存中压缩,再用软引用避免OOM,两种方式代码如下,大家可参考下:
方式一代码如下:
@SuppressWarnings("unused")
private Bitmap copressImage(String
File picture =
File(imgPath);
Options bitmapFactoryOptions =
BitmapFactory.Options();
//下面这个设置是将图片边界不可调节变为可调节
bitmapFactoryOptions.inJustDecodeBounds =
bitmapFactoryOptions.inSampleSize = 2;
outWidth =
bitmapFactoryOptions.outW
int outHeight =
bitmapFactoryOptions.outH
BitmapFactory.decodeFile(picture.getAbsolutePath(),
bitmapFactoryOptions);
float imagew = 150;
float imageh = 150;
yRatio = (int)
Math.ceil(bitmapFactoryOptions.outHeight
xRatio = (int)
.ceil(bitmapFactoryOptions.outWidth /
(yRatio & 1
|| xRatio & 1) {
if (yRatio & xRatio)
bitmapFactoryOptions.inSampleSize =
bitmapFactoryOptions.inSampleSize =
bitmapFactoryOptions.inJustDecodeBounds =
BitmapFactory.decodeFile(picture.getAbsolutePath(),
bitmapFactoryOptions);
if(bmap != null){
//ivwCouponImage.setImageBitmap(bmap);
return null;
方式二代码如下:
com.lvguo.scanstreet.
java.lang.ref.SoftR
java.util.ArrayL
java.util.HashM
java.util.L
android.app.A
android.app.AlertD
android.content.C
android.content.DialogI
android.content.I
android.content.res.TypedA
android.graphics.B
android.graphics.BitmapF
android.graphics.BitmapFactory.O
android.os.B
android.view.V
android.view.ViewG
android.view.WindowM
android.widget.AdapterV
android.widget.AdapterView.OnItemLongClickL
android.widget.BaseA
android.widget.G
android.widget.ImageV
android.widget.T
com.lvguo.scanstreet.R;
com.lvguo.scanstreet.data.ApplicationD
public class
PhotoScanActivity extends
Activity {
private Gallery gallery
List&String&
List&String& it
private ImageAdapter adapter
private String path
private String
private HashMap&String,
SoftReference&Bitmap&&
imageCache = null;
private Bitmap bitmap = null;
SoftReference&Bitmap& srf
public void
onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.photoscan);
Intent intent =
this.getIntent();
if(intent != null){
if(intent.getBundleExtra("bundle") != null){
Bundle bundle =
intent.getBundleExtra("bundle");
bundle.getString("path");
shopType =
bundle.getString("shopType");
private void
imageCache =
HashMap&String,
SoftReference&Bitmap&&();
(Gallery)findViewById(R.id.gallery);
ImageList =
if(ImageList.size() == 0){
Toast.makeText(getApplicationContext(), "无照片,请返回拍照后再使用预览",
Toast.LENGTH_SHORT).show();
adapter = new ImageAdapter(this, ImageList);
gallery.setAdapter(adapter);
gallery.setOnItemLongClickListener(longlistener);
private OnItemLongClickListener longlistener
OnItemLongClickListener() {
public boolean
onItemLongClick(AdapterView&?&
parent, View view,
position, long id)
//此处添加长按事件删除照片实现
AlertDialog.Builder
dialog = new
AlertDialog.Builder(PhotoScanActivity.this);
dialog.setIcon(R.drawable.warn);
dialog.setTitle("删除提示");
dialog.setMessage("你确定要删除这张照片吗?");
dialog.setPositiveButton("确定", new
DialogInterface.OnClickListener() {
public void
onClick(DialogInterface dialog, int which) {
File file = new
File(it.get(position));
boolean isS
if(file.exists()){
isSuccess =
file.delete();
if(isSuccess){
ImageList.remove(position);
adapter.notifyDataSetChanged();
//gallery.setAdapter(adapter);
if(ImageList.size() == 0){
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.phoSizeZero),
Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.phoDelSuccess),
Toast.LENGTH_SHORT).show();
dialog.setNegativeButton("取消",new
DialogInterface.OnClickListener() {
public void
onClick(DialogInterface dialog, int which) {
dialog.dismiss();
dialog.create().show();
return false;
List&String& getSD()
File fileK
ArrayList&String&();
if("newadd".equals(shopType)){
//如果是从查看本人新增列表项或商户列表项进来时
fileK = new
File(ApplicationData.TEMP);
//此时为纯粹新增
fileK = new File(path);
File[] files =
fileK.listFiles();
if(files != null &&
files.length&0){
for(File f : files
if(getImageFile(f.getName())){
it.add(f.getPath());
bitmapFactoryOptions = new
BitmapFactory.Options();
//下面这个设置是将图片边界不可调节变为可调节
bitmapFactoryOptions.inJustDecodeBounds =
bitmapFactoryOptions.inSampleSize = 5;
outWidth =
bitmapFactoryOptions.outW
int outHeight =
bitmapFactoryOptions.outH
float imagew = 150;
float imageh = 150;
yRatio = (int)
Math.ceil(bitmapFactoryOptions.outHeight
xRatio = (int)
.ceil(bitmapFactoryOptions.outWidth /
(yRatio & 1
|| xRatio & 1) {
if (yRatio & xRatio)
bitmapFactoryOptions.inSampleSize =
bitmapFactoryOptions.inSampleSize =
bitmapFactoryOptions.inJustDecodeBounds =
BitmapFactory.decodeFile(f.getPath(),
bitmapFactoryOptions);
//bitmap = BitmapFactory.decodeFile(f.getPath());
SoftReference&Bitmap&(bitmap);
imageCache.put(f.getName(), srf);
private boolean
getImageFile(String fName) {
String end =
.substring(fName.lastIndexOf(".") + 1, fName.length())
.toLowerCase();
(end.equals("jpg")
|| end.equals("gif") ||
end.equals("png")
end.equals("jpeg") ||
end.equals("bmp"))
re = true;
re = false;
public class
ImageAdapter extends
BaseAdapter{
mGalleryItemB
private Context
List&String&
public ImageAdapter(Context c,
List&String& li)
mContext =
TypedArray a =
obtainStyledAttributes(R.styleable.Gallery);
mGalleryItemBackground =
a.getResourceId(R.styleable.Gallery_android_galleryItemBackground,
a.recycle();
public int
getCount() {
return lis.size();
public Object getItem(int position) {
lis.get(position);
public long
getItemId(int
position) {
public View getView(int position, View convertView, ViewGroup
System.out.println("lis:"+lis);
File file = new
File(it.get(position));
SoftReference&Bitmap&
srf = imageCache.get(file.getName());
Bitmap bit =
srf.get();
ImageView i =
ImageView(mContext);
i.setImageBitmap(bit);
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setLayoutParams( new
Gallery.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT));
上面两种方式第一种直接使用边界压缩,第二种在使用边界压缩的情况下间接的使用了软引用来避免OOM,但大家都知道,这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,如果图片多且大,这种方式还是会引用OOM异常的,不着急,有的是办法解决,继续看,以下方式也大有妙用的:
InputStream is =
this.getResources().openRawResource(R.drawable.pic1);
BitmapFactory.Options
options=new
BitmapFactory.Options();
options.inJustDecodeBounds =
options.inSampleSize = 10;
//width,hight设为原来的十分一
Bitmap btp =BitmapFactory.decodeStream(is,null,options);
2. if(!bmp.isRecycle()
bmp.recycle()
//回收图片所占的内存
system.gc()
//提醒系统及时回收
上面代码与下面代码大家可分开使用,也可有效缓解内存问题哦...吼吼...
public static Bitmap
readBitMap(Context context, int resId){
BitmapFactory.Options opt = new
BitmapFactory.Options();
opt.inPreferredConfig =
Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
//获取资源图片
InputStream is = context.getResources().openRawResource(resId);
BitmapFactory.decodeStream(is,null,opt);
3:大家可以选择在合适的地方使用以下代码动态并自行显式调用GC来回收内存:
if(bitmapObject.isRecycled()==false)
//如果没有回收
bitmapObject.recycle();
4:这个就好玩了,优化Dalvik虚拟机的堆内存分配,听着很强大,来看下具体是怎么一回事
对于Android平台来说,其托管层使用的Dalvik
JavaVM从目前的表现来看还有很多地方可以优化处理,比如我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉GC处理,使用
dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率。当然具体原理我们可以参考开源工程,这里我们仅说下使用方法:
代码如下:
private final
floatTARGET_HEAP_UTILIZATION = 0.75f;
在程序onCreate时就可以调用
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
5:自定义我们的应用需要多大的内存,这个好暴力哇,强行设置最小内存大小,代码如下:
private final
static int CWJ_HEAP_SIZE
= 6* 1024*
//设置最小heap内存为6MB大小
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);
好了,文章写完了,片幅有点长,因为涉及到的东西太多了,其它文章小马都会贴源码,这篇文章小马是直接在项目中用三款安卓真机测试的,有效果,项目原码就不在这贴了,不然泄密了都,吼吼,但这里讲下还是会因为手机的不同而不同,大家得根据自己需求选择合适的方式来避免OOM,大家加油呀,每天都有或多或少的收获,这也算是进步,加油加油!
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 android系统开发 的文章

 

随机推荐