jvmGC机制及引用类型详解(一)——java四种引用类型

本系列文章内容:jvm内存模型;javaGC机制,以及不同种GC算法关系和区别;java引用类型概念,四种引用类型的区别范围;finalize方法介绍和FinalizeReference工作机制。


这篇文章是很早之前就想写的,当时是因为在android内存工具查看内存使用时发现FinalReference这个引用没接触过,想写一篇关于FinalReference的文章,但是看着看着觉得跟GC机制有很大的联系,于是我将这些内容放到一起写成几篇文章了,以下都是笔者的个人见解,如有不用意见可以一同讨论!


为什么要设置不同种引用类型?

java不同于C/C++,不是通过手动free函数释放对象,而是通过GC机制将对象释放工作交给GC线程去处理,所以我们需要知道让jvm知道我们希望哪些对象在什么时候释放,哪些对象还不能释放。这时候给不同类型对象设置不同的引用类型来达到这个效果。

为了满足在程序中对生命周期需求不同的对象,通过设置不同种引用类型,使内存利用率尽可能达到最大。

四种不同引用类型

java中有四种引用类型:强引用、软引用、弱引用、虚引用(或者叫幽灵引用)

当对象被强引用类型引用时,对象不会被gc释放。

当对象被软引用类型引用,且没有被强引用类型引用时,对象在jvm内存足够时gc不会被释放,在内存不足时gc会将其释放。

当对象被弱引用类型引用,且没有被强引用类型引用时,无论jvm内存是否充足,对象都会被释放。

当对象仅被虚引用类型引用时,对象类似于没有引用,随时会被释放。

从引用类型强度说:强引用>软引用>弱引用>虚引用。

 

强引用:

强引用是接触最多的,平时写一个A a = new A();就是将jvm栈中的引用内存a指向堆中新创建的A对象,这个就是强引用类型。

A a1 = new A();//新创建的A对象被强引用引用

其他三种引用都是通过包装对象的方式实现的,其对应的类分别SoftReference、WeakReference、PhantomReference。

软引用:

    public static void main(String[] args) throws InterruptedException, NoSuchFieldException {

        ArrayList> softReferences = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            softReferences.add(new SoftReference(new A()));
            System.gc();
        }

        for (SoftReference softReference : softReferences) {
            System.out.println(softReference.get());
        }

    }

    static class A {

        protected byte[] b;

        public A() {
            b = new byte[1024 * 1024 * 256];
        }
    }

为了模拟内存不足的情况,我在A对象创建时内部创建一个很大的数组,然后连续创建多个A对象,并用SoftReference引用起来。结果如下:

null
null
null
null
null
ClassTest$A@63947c6b
ClassTest$A@2b193f2d
ClassTest$A@355da254
ClassTest$A@4dc63996
ClassTest$A@d716361

Process finished with exit code 0

最开始创建的几个A对象已经被释放。

 

弱引用:

现在将所有的SoftReference换成WeakReference,结果如下:

    public static void main(String[] args) throws InterruptedException, NoSuchFieldException {

        A a;
        ArrayList> weakReferences = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            a = new A();//这里加了个引用哦
            weakReferences.add(new WeakReference(a));
            System.gc();
        }

        for (WeakReference weakReference : weakReferences) {
            System.out.println(weakReference.get());
        }

    }

    static class A {

        protected byte[] b;

        public A() {
            b = new byte[1024 * 1024 * 2];
        }
    }

我将代码做了一点修改,让引用a指向每次新创建的A对象,这样就保证每次新创建的A对象有强引用指向,就不会被释放掉,结果如下:

null
null
null
null
null
null
null
null
null
ClassTest$A@63947c6b

Process finished with exit code 0

可以看到在没有强引用指向的时候虚引用指向的对象全部被释放掉了。

 

虚引用:

虚引用和其他两个引用不同的地方在于构造方法,除了传入绑定的对象之外,还要传入一个引用队列。而且我们无法通过PhantomReference.get方法获取这个对象实例,在PhantomReference的get方法中复写实现就是返回null。

    public T get() {
        return null;
    }
        PhantomReference aPhantomReference = new PhantomReference<>(new A(), new ReferenceQueue<>());

要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。(引用自空谷幽澜的博客https://www.cnblogs.com/huajiezh/p/5835618.html)

简单来说,我们不指望通过虚引用操作实例,而是通过虚引用以及相关联的引用队列获取一个对象被回收的通知。

 

 

 

 

你可能感兴趣的:(java)