java 软引用、弱引用、强引用、虚引用的解析

来源:互联网 发布:世界粮农组织数据库 编辑:程序博客网 时间:2024/05/18 04:01

写了那么多篇文章第一次使用MarkDown编辑器。。。

  • 软引用与强引用、弱引用、虚引用的对比

软引用、强引用、弱引用、虚引用

  • 强引用
  • 弱引用
  • 虚引用
  • 软引用

强引用

强引用也就是我们一般使用的引用,如若一个对象有强引用,那么即使内存不足的情况出现,强引用对象也不会被轻易的回收

String s = new String();

创建了一个String对象,并用一个变量s存储对这个对象的引用。这就是个强引用。
变量持有的是这个对象的引用。通常,引用是一个对象的存储地址(但不同于c++、c,这个引用不能转换成整数)。而强引用其实就是正在使用的对象

虚引用

虚引用其实没什么用,和弱引用一样不会介入引用对象的生命周期。它的get方法总是返回null,所以你得不到它引用的对象。它的唯一作用就是跟踪对象合适被回收,但是必须要和引用队列 (ReferenceQueue)联合使用。当垃圾回收机制准备回收一个对象的时候,如果发现这个对象还存在一个虚引用,就会在这个对象被回收之前将这个虚引用加入到引用队列中,如果在引用队列中发现虚引用,那么就说明与之关联的对象将要被回收了。

弱引用

如果一个对象持有的是弱引用,垃圾回收机制是会去回收这个对象的,但是垃圾回收是优先级很低的线程,所以不一定很快能被回收。这个会在什么情况应用呢?看过android官网图片加载demo的同学们应该就比较能理解,我这里就直接拿过来用,作为弱引用的讲解例子。

class BitmapWorkerTask extends AsyncTask {    private final WeakReference imageViewReference;    private int data = 0;    public BitmapWorkerTask(ImageView imageView) {        // Use a WeakReference to ensure the ImageView can be garbage collected        imageViewReference = new WeakReference(imageView);    }    // Decode image in background.    @Override    protected Bitmap doInBackground(Integer... params) {        data = params[0];        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));    }    // Once complete, see if ImageView is still around and set bitmap.    @Override    protected void onPostExecute(Bitmap bitmap) {        if (imageViewReference != null && bitmap != null) {            final ImageView imageView = imageViewReference.get();            if (imageView != null) {                imageView.setImageBitmap(bitmap);            }        }    }}

这里就有一个imageview的弱引用,AsyncTask 这个大家都应该不陌生就不再介绍,这里在这个异步加载线程中加载图片,然后显示到imageview上面。而imageview是在ui线程(也就是主线程),我们不能保证用户什么时候会结束这个主线程,而此时已经启动了这个图片异步加载线程,我们不知道它什么时候会加载完,如果线程拥有的是imageview的强引用的话,这个imageview在用户退出的时候就不能被回收,也就会造成内存泄露的情况,在使用handler的时候也经常会遇到这个问题,就是你在activity中启动一个线程,使用handler处理信息,如果activity退出,而线程是不会退出的,还会持有handler对象,也就造成了handler不能被回收,内存泄露就出现了。而弱引用就解决了这个问题,像上面的代码,ui线程结束imageview此时要回收,因为图片加载线程中持有的是imageview的弱引用,他不会影响他所弱引用对象的回收,也就不会造成内存泄露,而此时如果弱引用的对象已经被回收,imageViewReference.get();得到的就是空

软引用

软引用在内存优化、速度优化中是经常会被用到的,软引用可用来实现内存敏感的高速缓存。如果一个对象有一个软引用,那么只要内存空间足够,他就不会被回收,我们就可以使用这些对象,相当于回收利用吧。我们知道开辟内存的消耗还是比较大的(不管是事件还是空间上),所以复用就可以做到减少内存的消耗,速度优化等。可以和引用队列联合使用。
这里要再次说下android官网的图片加载demo,还是建议大家去看看。

http://developer.android.com/intl/zh-cn/training/displaying-bitmaps/index.html

这里又要用到这里面的例子程序来讲软引用,但是他的demo中没有结合引用队列使用,下面就结合demo介绍:
首先来说说他的应用场景:异步加载大量的图片,大量图片的话就必定会用到内存缓存以提高速度。使用的是LruCache来缓存图片,也就是结合了最近最久未使用算法的缓存,这个缓存要有大小限制的(要是把全部内存都占了那肯定不行),也就是会有图片从这个缓存中移除,这个时候就用到了软引用,将那些所有被移出缓存的图片(bitmap对象)绑定软引用,加入到一个容器中存储,然后等到需要新的bitmap对象的时候就从这个容器中循环拿可以使用的进行复用(创建新的bitmap内存很耗时,也很费内存,复用大大提高效率)。
下面就是被移除的对象添加软引用的代码

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {    // Notify the removed entry that is no longer being cached.    @Override    protected void entryRemoved(boolean evicted, String key,            BitmapDrawable oldValue, BitmapDrawable newValue) {        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {            // The removed entry is a recycling drawable, so notify it            // that it has been removed from the memory cache.            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);        } else {            // The removed entry is a standard BitmapDrawable.            if (Utils.hasHoneycomb()) {                // We're running on Honeycomb or later, so add the bitmap                // to a SoftReference set for possible use with inBitmap later.                mReusableBitmaps.add                        (new SoftReference<Bitmap>(oldValue.getBitmap()));            }        }    }....

循环找可以复用的bitmap

protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {        Bitmap bitmap = null;    if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {        synchronized (mReusableBitmaps) {            final Iterator<SoftReference<Bitmap>> iterator                    = mReusableBitmaps.iterator();            Bitmap item;            while (iterator.hasNext()) {                item = iterator.next().get();                if (null != item && item.isMutable()) {                    // Check to see it the item can be used for inBitmap.                    if (canUseForInBitmap(item, options)) {                        bitmap = item;                        // Remove from reusable set so it can't be used again.                        iterator.remove();                        break;                    }                } else {                    // Remove from the set if the reference has been cleared.                    iterator.remove();                }            }        }    }    return bitmap;}

上面我们说了要结合引用队列使用,但是上面的而梨子中其实没有用到。先说说引用队列的用处吧:

String s = new String();ReferenceQueue queue = new  ReferenceQueue();SoftReference  ref=new  SoftReference(s, queue);

如上述代码 ,一开始s是强引用,然后创建了一个引用队列,以及结合了引用队列的软引用,此时如果s=null;(上面也讲到了,垃圾回收是一个优先级很低的线程,所以不会那么快回收),但这里假设s被回收了,那么ref就会加入到queue中,此时ref通过get方法得到的结果已经是null了,我们可以利用这个来清除没有用的软引用,如果像上面这样是单个的软引用对象的话就可以不用,但是很多时候我们会有一堆软引用然后都放在容器中,这个时候就需要去清理其中没有用的软引用,因为如果那个存软引用的容器存了一堆没用的,也是很大的消耗。
引用队列的使用以及为什么要使用也说了,为什么上面android官网的加载图片的demo中没有用到引用队列大家结合一下代码看看就知道了,引用队列就一个用处:清理没用的软引用,而上面的demo中我们可以看到他循环读取其中的软引用,判断是否为空(也就是没用了的软引用)等条件,不符合就直接移除了,也就做了清理没用的软引用的工作。

讲完了。。。最后要说,软引用、弱引用真的是比较好用的,一定要学会灵活使用。

0 0
原创粉丝点击