Java/Android中的强引用、软引用、弱引用、虚引用

转自:
Java/Android中的强引用、软引用、弱引用、虚引用


引用分为四个,从高到低的级别以此为强引用-软引用-弱引用-虚引用.

  • 引用类型
类别 回收机制 用途 生存时间
强引用 从不回收 对象状态 JVM停止运行时
软引用SoftReference 内存不足时进行回收 缓存 内存不足
弱引用WeakReference 对象不被引用时回收 缓存 GC运行后
虚引用 对象被回收时 管理控制精确内存稳定性 unknown

1.强引用:

Qiang qiang = new Qiang();
Niu niu = new  Niu(qiang);

强引用例子,niu持有qiang的引用,当qiang=null的时候,此时GC 应该去回收Qiang,但是由于之前的实例,被niu持有,因此,并不能回收,导致内存泄漏,典型的引用泄漏.

2.软引用SoftReference

    A a = new A();
    SoftReference aSoftRef = new SoftReference<>(a);
    A a1 = aSoftRef.get();

一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
只要垃圾回收器没有回收它,该对象就可以被程序使用。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

    A a = new A();
    ReferenceQueue refQueue = new ReferenceQueue();
    SoftReference aSoftRef = new SoftReference<>(a, refQueue);
    A a1 = aSoftRef.get();

3.Android中的示例:

  • 案例1:
    在平常的Android开发者,有很多的图片要显示,如果是网络图片,则需要通过网络解析获取,但是,每次都从网络解析图片,会影响体验,于是我们将会将请求到的图片保存到本地.
    但是,这就产生了另一个问题:每次我们从本地获取,相对与我们从网络请求后,直接从内存获取,效率更低.于是我们就还要在内存也缓存一份(层级著名的LRUCache 就是这3级缓存)
    但是因为图片的数量多,消耗内存过大,内存缓存的过程需要大量的内存,内存不够就会OOM,这时,便可以采用软引用的技术解决问题

    private Map softReferenceMap = new HashMap<>();

    /**
     * 
     * @param path
     */
    public void addBitmap(String path) {
        //强引用的bitmap对象
        //或者从网络请求图片Bitmap
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        //软引用的Bitmap对象
        SoftReference softBitmap = new SoftReference<>(bitmap);
        //添加软引用的Bitmap到map中时期缓存
        softReferenceMap.put(path, softBitmap);
    }


    public Bitmap getBitmap(String path) {
        //从缓存中取软引用的bitmap对象
        SoftReference softBitmap = softReferenceMap.get(path);
        //判断 软引用的 对象 是否已经被回收
        if (null == softBitmap) {
            return null;
            //TODO: 进行从SD卡读取的操作
        }
        //取出Bitmap对象,如果内存不足,软引用对象被回收了,将取到 null
        Bitmap bitmap = softBitmap.get();
        return bitmap;
    }

在softBitmap.get()中获取Bitmap的实例的强引用,在内存充足的情况下不会回收软引用对象,可以取出bitmap

内存不足时,softBitmap.get()不在返回bitamp直接返回null,软引用被回收了

因此在获取Bitmap的对象之前要判断softBitmap == null是否为空,否则将会出现空指针异常.
这里,还可以扩展从SD卡读取缓存的操作

  • 案例2:
    设计给出了一张1080的全屏图片,这张图片假设500K,可以想象它的内存消耗,通用使用软引用来解决
    private void test() {
        ImageView imageView = findViewById(R.id.test_iv);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_4444;
        //5.0后失效 设置了也没有
        options.inPurgeable=true;
        options.outWidth=720;
        options.outHeight = 1280;
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
                R.mipmap.avatar,
                options);
        Drawable drawable = new BitmapDrawable(getResources(), bitmap);
        SoftReference drawableSoftReference = new SoftReference(drawable);
            //判断 是否 null 如果内存不足,软引用 被回收,那么就不展示在ImageView上
        if (drawableSoftReference != null) {
            imageView.setBackground(drawableSoftReference.get());
        }
    }

因此在特定的场景想要避免OOM的发生就尽量使用软引用吧.

但是在Android中最好选择Least Recently Used(LRU),在它内部维护一个特定大小内存,在内存不足时会根据一系列的策略算法来进行处理移除掉当前一些缓存以便获取新的内空间用来缓存数据.

4.弱引用WeakReference

 WeakReference weakReference = new WeakReference(new MainActivity()) ;

如果一个对象只具有弱引用,那么在垃圾回收线程扫描的过程中,一旦发现了,只有弱引用的对象,不管当前内存空间是否足够,都会回收他的内存.
不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现哪些只具有弱引用的对象.
弱引用可以喝一个引用对列(ReferenceQueue)配合使用,如果弱引用所引用的对象被垃圾回收,java虚拟机就会把这个弱引用加入到与之关联的引用对列之中.

5.Android中的示例

比如一个Activity持有一个Handler,handler是匿名内部类的形式,而非,静态内部类,那么handler就会持有此activity的引用,Handler作为一个耗时的异步线程的通信工具,如果在异步线程处理任务过程中,Activity关闭了,此时Activity应该被回收,但是因为Handler还持有Activity的引用,而了另一个"异步线程"持有handler的引用,那么,就将导致内存泄漏

  • 解决方案:
      • 在Activity关闭的地方,停止线程,并把handler的消息队列的所有消息对象移除
      • Handler改为静态类
public class TestActivity extends AppCompatActivity {

    private MyHandler mHandler = new MyHandler(this);

    private static class MyHandler extends Handler {
        private WeakReference ctxRef;

        public MyHandler(Context context) {
            ctxRef = new WeakReference<>(context);

        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (ctxRef.get() instanceof TestActivity) {
                ((TestActivity) ctxRef.get()).textView.setText("");
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

}

6.虚引用:

在对象销毁时会被回收.

在Java中GC的运行时间是不确定的,在Java里有一个finalize方法,在垃圾回收器准备释放内存的时候,会先调用finalize().但因为内存还没有好耗尽,拟机不能保证在适当的时机调用finalize,因此垃圾回收期与finalize是不可靠的方法.

这时可以采用虚引用.比如一个固定的内存,在明确知道一个bitmap回收之后会释放一部分内存,新释放开辟的内存就可以让其他bitmap来使用,以此循环来达到内存的稳定性控制.

7.总结

在Android中比较常用便是 弱引用和软引用了。

对于一些OOM等常规处理使用软引用便可很好的解决,可以实现高速缓存.

对于偶尔使用的对象,并且随时获取到便使用弱引用来标记

软引用与弱引用的区别:只含有弱引用的对象的生命周期更短。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

虚引用只要用于内存的精准控制,如Android中的ViewPager图片的查看等等

你可能感兴趣的:(Java/Android中的强引用、软引用、弱引用、虚引用)