JDK核心JAVA源码解析(3) - 引用相关

想写这个系列很久了,对自己也是个总结与提高。原来在学JAVA时,那些JAVA入门书籍会告诉你一些规律还有法则,但是用的时候我们一般很难想起来,因为我们用的少并且不知道为什么。知其所以然方能印象深刻并学以致用。

本篇文章针对引用分析,是后面分析各种框架机制的基础

Java引用相关

强引用(Strong Reference)

强引用就是指在程序代码之中普遍存在的,一般的new一个对象并赋值给一个对象变量,就是一个强引用;只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。

Object object = new Object();
String str = "hello";

如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。很多JAVA集合类通过这种方式来移除一个对象,例如ArrayList:

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

软引用(Soft reference)

软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。
对于软引用关联着的对象,如果内存充足,则垃圾回收器不会回收该对象,如果内存不够了,就会回收这些对象的内存。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中

软引用初始化与测试

public class SoftReferenceDemo {
    public static void main(String[] args) {
        //初始化,声明强引用
        TestNormalObject testNormalObject = new TestNormalObject();
        //声明软引用
        SoftReference<TestNormalObject> testNormalObjectSoftReference = new SoftReference<>(testNormalObject);
        //初始化,声明强引用
        TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
        //声明软引用
        SoftReference<TestFinalizeObject> testFinalizeObjectSoftReference = new SoftReference<>(testFinalizeObject);

        //强引用去掉。让对象只剩下软引用
        testNormalObject = null;
        testFinalizeObject = null;

        //看是否还存在
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());

        try {
            //我们启动参数设置的是-Xmx16m,所以这里一定会抛出内存溢出的错误
            //也一定会触发回收软引用
            byte[] test = new byte[1024 * 1024 * 16];
        } catch (Error e) {
            System.out.println(e.getClass().getName() + ":" + e.getMessage());
        }

        //看是否还存在
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());
    }

    /**
     * 第一种类。未覆盖finalize
     */
    public static class TestNormalObject {
    }

    /**
     * 第二种类。覆盖finalize的类
     */
    public static class TestFinalizeObject {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Finalize is called");
        }
    }
}

输出为:

SoftReferenceDemo$TestNormalObject@31b7dea0
SoftReferenceDemo$TestFinalizeObject@3ac42916
Finalize is called
java.lang.OutOfMemoryError:Java heap space
null
null

看一个软引用是否被垃圾回收一种方式是通过get()方法看返回是否为null判断,或者在构造的时候传入一个队列,之后根据这个队列中有没有这个对象来判断是否被回收了

public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<TestNormalObject> testNormalObjectReferenceQueue = new ReferenceQueue<>();
        ReferenceQueue<TestFinalizeObject> testFinalizeObjectReferenceQueue = new ReferenceQueue<>();

        //初始化,声明强引用
        TestNormalObject testNormalObject = new TestNormalObject();
        //声明软引用
        SoftReference<TestNormalObject> testNormalObjectSoftReference = new SoftReference<>(testNormalObject, testNormalObjectReferenceQueue);
        //初始化,声明强引用
        TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
        //声明软引用
        SoftReference<TestFinalizeObject> testFinalizeObjectSoftReference = new SoftReference<>(testFinalizeObject, testFinalizeObjectReferenceQueue);

        System.out.println("Origin: " + testNormalObjectSoftReference);
        System.out.println("Origin: " + testFinalizeObjectSoftReference);


        Thread thread1 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testNormalObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread1.start();
        Thread thread2 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testFinalizeObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread2.start();

        //强引用去掉。让对象只剩下软引用
        testNormalObject = null;
        testFinalizeObject = null;

        //看是否还存在
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());

        try {
            //我们启动参数设置的是-Xmx16m,所以这里一定会抛出内存溢出的错误
            //也一定会触发回收软引用
            byte[] test = new byte[1024 * 1024 * 16];
        } catch (Error e) {
            System.out.println(e.getClass().getName() + ":" + e.getMessage());
        }

        //看是否还存在
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());

        thread1.join();
        thread2.join();
    }

    /**
     * 第一种类。未覆盖finalize
     */
    public static class TestNormalObject {
    }

    /**
     * 第二种类。覆盖finalize的类
     */
    public static class TestFinalizeObject {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Finalize is called");
        }
    }
}

这时候,输出并不确定,有时候是:

Origin: java.lang.ref.SoftReference@31b7dea0
Origin: java.lang.ref.SoftReference@3ac42916
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestNormalObject@22a71081
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestFinalizeObject@3930015a
java.lang.OutOfMemoryError:Java heap space
null
null
GOT From Queue: null
GOT From Queue: null
Finalize is called

有时候是:

Origin: java.lang.ref.SoftReference@31b7dea0
Origin: java.lang.ref.SoftReference@3ac42916
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestNormalObject@22a71081
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestFinalizeObject@3930015a
java.lang.OutOfMemoryError:Java heap space
null
null
Finalize is called
GOT From Queue: null
GOT From Queue: null

Finalize is called还有GOT From Queue: null的先后顺序不确定,还有其他各种结果,出现这样的原因除了因为多线程,还有就是:对于软引用,一旦发生GC,并且在系统将要发生内存溢出异常之前,只要这个对象只有软引用,就会被放入队列中。放入队列后,才会真正被回收

弱引用 WeakReference

用来描述非必须的对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
在java中,用java.lang.ref.WeakReference类来表示

弱引用初始化与测试

public class WeakReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<TestNormalObject> testNormalObjectReferenceQueue = new ReferenceQueue<>();
        ReferenceQueue<TestFinalizeObject> testFinalizeObjectReferenceQueue = new ReferenceQueue<>();

        TestNormalObject testNormalObject = new TestNormalObject();
        WeakReference<TestNormalObject> testNormalObjectWeakReference = new WeakReference<>(testNormalObject, testNormalObjectReferenceQueue);
        TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
        WeakReference<TestFinalizeObject> testFinalizeObjectWeakReference = new WeakReference<>(testFinalizeObject, testFinalizeObjectReferenceQueue);

        System.out.println(testNormalObjectWeakReference);
        System.out.println(testFinalizeObjectWeakReference);

        //强引用去掉。让对象只剩下软引用
        testNormalObject = null;
        testFinalizeObject = null;

        Thread thread1 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testNormalObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread1.start();
        Thread thread2 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testFinalizeObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread2.start();

        //显示请求GC,但是不一定会立刻GC
        System.gc();

        thread1.join();
        thread2.join();
    }

    /**
     * 第一种类。未覆盖finalize
     */
    public static class TestNormalObject {
    }

    /**
     * 第二种类。覆盖finalize的类
     */
    public static class TestFinalizeObject {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Finalize is called");
        }
    }
}

输出类似于:

java.lang.ref.WeakReference@6d8acf
java.lang.ref.WeakReference@182830e
Finalize is called
GOT From Queue: null
GOT From Queue: null

对于弱引用,只要这个对象只有弱引用,就会被放入队列中。放入队列后,才会真正被回收,并且是在只要有GC的时候就会被回收

虚引用 Phantom Reference

虚引用比较特殊,它只用于记录一个对象是否已经被回收了。GC流程很复杂,而且Java程序员无法知道究竟一个可以被GC的对象到底有没有确定被回收。这时候,我们就需要虚引用。

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

虚引用和弱引用还有软引用不同,虚引用只有在对象的finalize方法被调用后(如果类覆盖了这个finalize方法),并且已经被回收之后,才会进入初始化时的ReferenceQueue。而另外两种引用是先进入ReferenceQueue之后对象的finalize方法被调用之后回收。

思考finalize()

首先我们来看一下如果实现了finalize()会对GC有何影响,针对Mark-Sweep方式的GC来说,个unreachable的对象如果是finalizable的,Minor GC知道不能马上杀掉,需要先执行finalize()方法。但finalize()方法Minor GC自己又不能执行。需要Finalizer的finalizer daemon thread线程负责执行。所以Minor GC没办法,只好先把它插入到finalization queue。等以后什么时候finalizer daemon thread接手了,会一个个执行队列里对象的finalize()方法。所以,需要两轮GC才能将这个对象回收掉。
还有,如果finalize()执行时间特别长,慢于生成对象的速度,那么还会造成内存溢出的风险。
所以总之,不要用finalize()!!!

总结

  • 强引用:普通声明的对象赋予变量就是强引用,即使OOM也不会被回收
  • 软引用:要触发OOM时会被回收
  • 弱引用:只要有GC发生就会被回收
  • 虚引用:专门用来标记一个对象是否被回收的引用,注意初始化一定要传入一个队列,否则没意义。虚引用和弱引用还有软引用不同,虚引用只有在对象的finalize方法被调用后(如果类覆盖了这个finalize方法),并且已经被回收之后,才会进入初始化时的ReferenceQueue。而另外两种引用是先进入ReferenceQueue之后对象的finalize方法被调用之后回收。
  • finalize():不要用finalize()!!!

猜你喜欢

转载自blog.csdn.net/zhxdick/article/details/80991802