一.这个知识出现的目的是什么

解决内存泄露和OOM的问题
解合理的使用引用可以帮助垃圾回收器更好的管理Java内存

二.这个知识的应用场合是什么

在可能造成内存泄露的场景下
oom
因为软引用、弱引用、虚引用,不会和它关联的对象造成引用,所以不会对和他们关联的对象的生命周期产生影响。

特别注意,在世纪程序设计中一般很少使用弱引用与虚引用,使用软用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

三.这个知识的基本概念

1.强引用(StrongReference)

  解强引用是我们在编程过程中使用的最简单的引用,如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用。任何被强引用指向的对象都不能被垃圾回收器回收,这些对象都是在程序中需要的。

2.软引用(SoftReference)

  解软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

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

3.弱引用(WeakReference)

  解弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。

4.虚引用(PhantomReference)

  解虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。

  解要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

四.一个demo

public class Demo2 {
    public static void main(String[] args) {
        // 软引用,没有队列,可以有
        String hello = new String("hello");//强引用
        SoftReference sr = new SoftReference(hello);
        hello = "111";
        System.out.println(sr.get());

        // 弱引用,没有队列,可以有
        String hello1 = new String("hello");
        WeakReference<String> sr1 = new WeakReference<String>(hello1);
        hello1 = "222";
        System.out.println(sr1.get());
        System.gc();
        System.out.println(sr1.get());

        // 虚引用,有队列
        ReferenceQueue<String> queue = new ReferenceQueue<>();
        String hello2 = new String("hello");
        PhantomReference<String> pr = new PhantomReference<>(hello2, queue);
        hello2 = "333";
        System.out.println(pr.get());
    }



}

五.一个应用场景

  解下面举个例子,假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,此时使用软引用可以解决这个问题。

  解设计思路是:用一个HashMap来保存图片的路径 和 相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。在Android开发中对于大量图片下载会经常用到。

下面这段代码是摘自博客:

.....
private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
<br>....
public void addBitmapToCache(String path) {

        // 强引用的Bitmap对象

        Bitmap bitmap = BitmapFactory.decodeFile(path);

        // 软引用的Bitmap对象

        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);

        // 添加该对象到Map中使其缓存

        imageCache.put(path, softBitmap);

    }

 public Bitmap getBitmapByPath(String path) {

        // 从缓存中取软引用的Bitmap对象

        SoftReference<Bitmap> softBitmap = imageCache.get(path);

        // 判断是否存在软引用

        if (softBitmap == null) {

            return null;

        }

        // 取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空

        Bitmap bitmap = softBitmap.get();

        return bitmap;

    }

六.对于java引用的理解

1.Java引用与Java垃圾回收机制的关系

  解当Java虚拟机(JVM)觉得内存不够用的时候,会触发垃圾回收操作(GC),清除无用的对象,释放内存。可是如何判断一个对象是否是垃圾呢?其中的一个方法是计算指向该对象的引用数量,如果引用数量为0,那么该对象就为垃圾(Thread对象是例外),否则还有用处,不能被回收。但是如果把引用数为0的对象都回收了,还是不能满足内存需求怎么办?Java把引用分为4种类型,垃圾回收器会尝试回收只有弱引用的对象。
  解按照一个对象的引用可达(Reachable)强度,由强到弱分为5类,如下:

  1. 强可达(Strong Reachable)
    在一个线程内,无需引用直接可达,新创建的对象是强可达的。
  2. 软可达(Soft Reachable)
    不是强可达的,但是通过一个软引用(SoftReference)可达。
  3. 弱可达(Soft Reachable)
    既不是强可达也不是软可达,但是通过一个弱引用(WeakReference)可达。
  4. 虚可达(Phantom Reachable)
    既不是强可达,不是软可达,也不是弱可达,但是通过一个虚引用(PhantomReference)可达。
  5. 不可达(Unreachable)
    没有任何引用指向对象。
    比较好、容易理解的是Java垃圾回收器会优先清理可达强度低的对象。另外有两个重要的点:
    强可达的一定不会被清理
    JVM保证抛出out of memory之前,清理所有的软引用对象

2.我的理解和感悟

  引用是java中非常重要的概念,理解它对写出高质量的代码非常重要,否则写出的代码都是各种内存泄露、oom却不自知。而要想搞明白这个概念,需要对jvm有一定的了解,包括内存分配和垃圾回收。

  1. 局部变量不持有对外部对象(即外部对象分配的内存区域)的的引用,因为局部变量只在它的作用区域内有效。
  2. 成员变量不一定持有对外部对象((即外部对象分配的内存区域))的引用,只有当成员变量是内部类的时候才会出现这个问题,如果成员变量是定义在其他的文件中,不会产生内存泄露,要注意是否可能造成内存泄露的问题。内部类才会持有对外部类的引用。即:1 内部类对象的创建依赖于外部类对象;2 内部类对象持有指向外部类对象的引用。
  3. 引用的简单理解:A a = new A(),java会在堆中分配一块区域,然后让位于栈中的引用a指向这块区域。
  4. 其实垃圾回收机制关注的是堆中的分配的一块区域是否还被其他引用变量指着,如果没有人指着,就可以回收了。
// 软引用,没有队列,可以有
        String hello = new String("hello");//强引用
        SoftReference sr = new SoftReference(hello);
        hello = null;
        System.out.println(sr.get());

如上代码所示,hello指向堆中分配一块内存,软引用也指向了这块内存,这是hello清除了这个指向,所以这块内存区域目前只有软引用指向他,这个时候这块内存区域不会立即被回收,除非JVM需要内存才会被回收。

// 弱引用,没有队列,可以有
        String hello1 = new String("hello");
        WeakReference<String> sr1 = new WeakReference<String>(hello1);
        hello1 = null;
        System.out.println(sr1.get());

如上代码所示,hello1指向堆中分配一块内存,软引用也指向了这块内存,这是hello1清除了这个指向,所以这块内存区域目前只有弱引用指向他,当JVM进行垃圾回收时,无论内存是否充足,都会回收这块内存区域。

// 虚引用,有队列
        ReferenceQueue<String> queue = new ReferenceQueue<>();
        String hello2 = new String("hello");
        PhantomReference<String> pr = new PhantomReference<>(hello2, queue);
        hello2 = null;
        System.out.println(pr.get());

如上代码所示,hello2指向堆中分配一块内存,软引用也指向了这块内存,这是hello2清除了这个指向,所以这块内存区域目前只有虚引用指向他,而虚引用则跟没有引用与之关联一样,这块内存区域在任何时候都可能被垃圾回收器回收。
5. 以后我们再说对象是否被回收,建议说成分配的区域是否被回收,这样更加直指问题的核心。