1.1 概述
java中垃圾回收器(GC)的核心思想:对虚拟机可用的内存空间,即堆空间中的对象进行识别,如果对象不再被引用,则GC回收其所占用的内存空间用于再分配。
1.2 OutOfMemory大户----Bitmap
Bitmap:图片资源一般非常消耗内存。Android系统中分给虚拟机中的图片堆栈大小只有8MB,如果超出了,则会OOM。
1.2.1 及时回收Bitmap的内存
Java有自己GC,为什么还要显示的调用recycle()呢?
答:Bitmap类的构造方法是私有的,所以不能new一个新对象,只能通过BitmapFactory的各种静态方法来实例化一个Bitmap。由BitmapFactory的源码可知,生成Bitmap是通过JNI来实现的。所以加载Bitmap到内存空间包含两部分内存区域:Java部分和C部分。Java部分不用的时候会有GC来回收,但是C部分只能通过调用底层的功能来释放。所以这里需要通过recycle()代码里调用JNI来释放C部分的内存。
虽然系统可能为你释放Bitmap的内存空间,但是,最好由自己及时释放。
//先判断是否已经回收if(bitmap != null && !bitmap.isRecycled()) { //回收并且置为null bitmap.recycle(); bitmap = null;}
1.2.2 捕获异常
为了避免在分配Bitmap内存的时候出现OOM异常而导致程序Crash掉,通常,需在Bitmap实例化部分进行OOM异常的捕获,如果捕捉到OOM异常,则返回一个默认的Bitmap图。
Bitmap bimap = null;try { //实例化Bitmap bitmap = BitmapFactory.decodeFile(path); }catch(OutOfMemoryError e) { //...... }if(bitmap == null) { return defaultBitmapMap; }
注意:很多开发者会习惯性的在上面代码中捕获Exception。但是可能让他们失望了,因为OutOfMemoryError从名字就可以看出来是一种Error,而不是Exception。
1.2.3 缓存通用的Bitmap对象
有时候,可能需要在一个应用中多次用到同一张图片,比如用户图像列表等。而用户没有设置头像的话,则会显示一个默认的头像,而这个头像是存放在资源文件中的。
诸如此类的情况,我们就可以对同一Bitmap进行缓存。如果不缓存,则每次实例化得到的都是不同Bitmap对象,这样会造成内存的浪费。
缓存一般有两个级别:
硬盘缓存:比如开发网络应用中,可以将一些从网络上获取的数据保存到SD卡中,下次直接从SD卡获取;
内存缓存:比如应用程序中经常用到同一对象,则可以把它放到内存中缓存起来,需要的时候从内存中直接获取。
1.2.4 压缩图片
在上面已经提到过Android系统给图片的堆栈空间就8M,所以如果图片像素过大,超出此限制,则必定会造成OOM。这个时候,通常需要将图片缩小,以减少图片载入过程中内存的使用。
压缩图片这一系列操作要用到BitmapFactory.Options。
//获得Options对象BitmapFactory.Options opts = new BitmapFactory.Options();//设置inJustDecodeBounds为true,保证生成的Bitmap对象为nullopts.inJustDecodeBounds = true;//使用decodeFile方法为了获得图片的宽和高BitmapFactory.decodeFile(path,opts);//打印图片的宽和高Log.d("example",opts.outWidth + "," + opts.outHeight);
上面代码获得图片宽高之后,判断图片是否需要压缩,如果需要压缩,则设置Options的inSampleSize属性,比如设置为2,即将图片的宽高分别缩小为原来的1/2,则整个图片缩小1/4。不需要压缩,当然将inSampleSize设置为1。需要注意的是。在下次使用BitmapFactory的decodeFile()等方法实例化Bitmap对象前,别忘了将inJustDecodeBound设置为false,否则得到的Bitmap对象为null。
对于本地或自己服务器上的图片的大小自己可能还心里有数,但是特别对于来自外界,比如网络上的图片,获取前判断是否需要压缩就显得尤为重要了。