Java引用类型

  强软弱虚 四种引用类型

  1.Java中默认的就是强引用

public class T {

    /**
     * java垃圾回收时会调用一次且只调用一次
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize执行....");
    }
}
*********************************
public class StrongReferenceTest {
    public static void main(String[] args) {
        T t = new T();
        System.gc();
        System.out.println(t);
        t= null;
        System.gc();
        System.out.println(t);
    }
}
结果:

com.example.reference.T@30946e09
null
finalize执行....

Process finished with exit code 0

   强应用,那么如果对象有指向,垃圾回收器将不会回收它。

  手动置t为null的话GC回收就会回收该对象

  正常情况下,在方法内部有一个强引用,这个引用保存在栈中,而真正的应用内容T保存在堆中,当方法运行结束,就会退出方法栈,引用的变量t就会为null,这个对象就会被回收

  但是当为全局变量时,那就不会被回收了。

  例如ArrayList的clear()方法,是将每个elementData[i] == null 而非  elementData 对象置为null

  

  2.软引用(SoftReference)

/**
* -Xmx30M
*/
public class SoftReferenceTest {
public static void main(String[] args) {
ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024*1024*10],referenceQueue);
System.out.println(softReference.get()+"---"+softReference);
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(softReference.get()+"---"+softReference);
byte[] bytes2 = new byte[1024*1024*10];
System.out.println(softReference.get()+"---"+softReference);
Reference<? extends byte[]> poll = referenceQueue.poll();
System.out.println(poll);
}
}

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

  软引用使用场景,做缓存使用。

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

引用队列的作用:

  垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软引用对象,而且虚拟机会尽可能优先回收长时间闲置不用额软引用对象。

  对于哪些刚构建的或刚使用过的“较新”软对象会被虚拟机尽可能保留,所以引入引用队列ReferenceQueue的原因

[B@4459eb14---java.lang.ref.SoftReference@5a2e4553
[B@4459eb14---java.lang.ref.SoftReference@5a2e4553
null---java.lang.ref.SoftReference@5a2e4553
java.lang.ref.SoftReference@5a2e4553

  

 通过结果我们可以分析出,

  本身softReference连接SoftReference是强引用(第三行,softReference对象有值,)

  SoftReference连接byte[] 是软引用,内存不够后,gc回收(第三行,softReference.get() 结果为null

垃圾回收也只清除软引用的连接,清除不了强引用

  3.弱引用(WeakReference)

  弱引用与软引用的区别在于,只具有软引用的对象拥有更短暂的生命周期。

  在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存是否足够,都会回收内存。

public class WeakReferenceTest {
    public static void main(String[] args) {
//        WeakReference<T> weak = new WeakReference(new T());
        WeakReference<String> weak = new WeakReference(new String("abc"));
        System.out.println(weak.get() + "---" + weak);
        System.gc();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(weak.get() + "---" + weak);
        System.out.println("***************************");
        System.out.println(weak.get() + "---" + weak);
    }
}

abc---java.lang.ref.WeakReference@4459eb14
null---java.lang.ref.WeakReference@4459eb14
***************************
null---java.lang.ref.WeakReference@4459eb14

  第一次打印出来,第二次打印之前调用gc,所以会打印null值,

  weak指向WeakReference弱引用对象,弱引用对象指向new String()对象,gc会把后者的关系给回收。

实际使用场景ThreadLocal 

  

package com.example.reference;

public class ThreaLocalTest {
    private static ThreadLocal<String> threadLocal= new ThreadLocal<>();
    public static void main(String[] args) throws Exception {
        new Thread(()->{
                threadLocal.set(Thread.currentThread().getName()+"-----哈哈");
                System.out.println("线程1"+threadLocal.get());
        }).start();
        new Thread(()->{
                System.out.println("线程2"+threadLocal.get());
        }).start();
        Thread.sleep(500);
        System.in.read();
    }
}
结果:
线程1Thread-0-----哈哈
线程2null

  说明第一个线程的set值干扰不到第二个线程的数据正确性

源码分析:

  

 set方法第一步获取当前线程,然后拿到ThreadLocalMap对象是一个key-value结构,key为this也就是ThreadLocal对象,所以最为关键的是ThreadLocalMap对象如何获取

  

 threadLocalMap对象是Thread类的一个属性,所以说天然不同线程互不干扰

   分析createMap发现内部new Entry()对象这个对象继承WeakReference 把key放置其中

  ThreadLocal关系图如上:

  在线程Thread中 定义一个变量 t1 强引用new一个对象ThreadLocal

    t1.set()会在Thread内部构建一个ThreadLocalMap对象用于存储信息

    存储的信息又是构建成一个Entry对象,而entry对象又是继承弱引用对象

    t1通过强引用指向ThreadLocal对象,而Map里面的key又是通过弱引用指向ThreadLocal对象。

      如果map里面的引用是强引用,那么就是t1使用结束后,退出栈后但是map里面仍然使用强引用关联导致ThreadLocal这个对象始终无法回收,缓存弱引用的话,只要一gc就回收

    假设t1的指向被回收,key指向也被回收,key指向一个null,但是这个map仍然存在,里面的value一直存在,且会一直变大。仍然最后导致内存泄漏。

      所以ThreadLocal提供了remove方法在方法使用后进行调用

  4.虚引用(PhantomReference) 

  虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收

  虚引用必须和引用队列联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中

  虚引用用来管理对外内存,而我们的gc回收的永远是jvm的内存触发不了堆外内存,如果使用虚引用可以以一种别的方式进行内存回收

public class PhantomReferenceTest {
    public static void main(String[] args) {
        ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<T> reference = new PhantomReference<>(new T(),queue);
        System.out.println(reference.get()+"----"+reference);
        Reference poll = queue.poll();
        System.out.println(poll.get());
    }
}
null----java.lang.ref.PhantomReference@4459eb14
Exception in thread "main" java.lang.NullPointerException
	at com.example.reference.PhantomReferenceTest.main(PhantomReferenceTest.java:13)

  

 

 

 

 

 

 

 

 https://blog.csdn.net/baidu_22254181/article/details/82555485

 

原文地址:https://www.cnblogs.com/huan30/p/12790411.html