Java中的 强引用、软引用、弱引用、虚引用使用

来源:互联网 发布:gbase数据库 编辑:程序博客网 时间:2024/06/17 09:33


强引用:

public class StrongRef {public static void main(String[] args) {Object obj = new Object();System.gc();System.out.println(obj);}}

其中obj就是强引用,强引用就是指在程序代码之中普遍存在的。只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。比如Vector类的clear方法中就是通过将引用赋值为null来实现清理工作的:

    public synchronized E remove(int index) {        modCount++;        if (index >= elementCount)            throw new ArrayIndexOutOfBoundsException(index);        E oldValue = elementData(index);        int numMoved = elementCount - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,                             numMoved);        elementData[--elementCount] = null; // Let gc do its work,便于来及回收        return oldValue;    }


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

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

    public SoftReference(T referent) {        super(referent);        this.timestamp = clock;    }    /**     * Creates a new soft reference that refers to the given object and is     * registered with the given queue.     *     * @param referent object the new soft reference will refer to     * @param q the queue with which the reference is to be registered,     *          or <tt>null</tt> if registration is not required     *     */    public SoftReference(T referent, ReferenceQueue<? super T> q) {        super(referent, q);        this.timestamp = clock;    }

接下来看演示例子:

例子1:

JVM参数配置为:-Xms10m -Xmx10m -Xmn3m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:SurvivorRatio=1 ,各个代大小可以下图直观看到


代码:

public class SoftRef { public static void main(String[] args) throws Exception {int mb = 1024 * 1024;System.out.println(mb);byte[] mb4 = new byte[mb * 4];SoftReference<Object> sr = new SoftReference<Object>(mb4); List list = new ArrayList<>();for (int i = 1; i < 10; i++) {System.out.println(i + "  --->  " + sr.get());list.add(new byte[mb]);}}}

执行输出如下:

1  --->  [B@280848502  --->  [B@280848503  --->  [B@28084850Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat com.daxin.ref.SoftRef.main(SoftRef.java:42)

发现实际for循环中就开辟了2MB的空间溢出了,是不是很好奇为什么软引用在溢出之前没有被GC?这个问题很容易犯!其实回归源码可以看到:

byte[] mb4 = new byte[mb * 4];SoftReference<Object> sr = new SoftReference<Object>(mb4);
我们传递给SoftReference的mb4其实是一个强引用,所以无法回收。如果修改代码如下:

public class SoftRef { public static void main(String[] args) throws Exception {int mb = 1024 * 1024;System.out.println(mb);SoftReference<Object> sr = new SoftReference<Object>(new byte[mb * 4]); List list = new ArrayList<>();for (int i = 1; i < 10; i++) {System.out.println(i + "  --->  " + sr.get());list.add(new byte[mb]);}}}

输出如下:

1  --->  [B@17b682152  --->  [B@17b682153  --->  [B@17b682154  --->  null5  --->  null6  --->  null7  --->  nullException in thread "main" java.lang.OutOfMemoryError: Java heap spaceat com.daxin.ref.SoftRef.main(SoftRef.java:40)


此时for循环开辟了6MB空间,最后溢出是由于堆大小不足导致,扩大堆大小即可。

或者修改代码如下:

public class SoftRef { public static void main(String[] args) throws Exception {int mb = 1024 * 1024;byte[] mb4 = new byte[mb * 4];SoftReference<Object> sr = new SoftReference<Object>(mb4);mb4=null;//中断强引用List list = new ArrayList<>();for (int i = 1; i < 10; i++) {System.out.println(i + "  --->  " + sr.get());list.add(new byte[mb]);}}}


添加到软引用之后,将其置为null。使其变为软引用可以达到同样效果


弱引用:

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


public class WeakRef { public static void main(String[] args) {             WeakReference<String> sr = new WeakReference<String>(new String("hello"));        System.out.println(sr.get());        System.gc();//通知JVM的gc进行垃圾回收        System.out.println(sr.get());    }}

输出打印:

hellonull


虚引用:

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

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


深入理解JVM虚拟机书中有一段话介绍虚引用的作用:“为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知”


示例代码:

import java.lang.ref.PhantomReference;import java.lang.ref.Reference;import java.lang.ref.ReferenceQueue;import java.lang.reflect.Field;public class PhantomRefDemo {public static boolean isRun = true;@SuppressWarnings("static-access")public static void main(String[] args) throws Exception {// 强引用String abc = new String("abc");System.out.println(abc.getClass() + "@" + abc.hashCode());final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();new Thread() {public void run() {while (isRun) {Object obj = referenceQueue.poll();if (obj != null) {try {Field rereferent = Reference.class.getDeclaredField("referent");rereferent.setAccessible(true);Object result = rereferent.get(obj);System.out.println("gc will collect:" + result.getClass() + "@" + result.hashCode() + "\t"+ (String) result);} catch (Exception e) {e.printStackTrace();}}}}}.start();PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc, referenceQueue);// 深入理解JVM书中原话:为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知// 将abc强引用转成弱引用,然后可以自行处理回收通知abc = null;System.gc();Thread.currentThread().sleep(1000);isRun = false;}}

阅读全文
0 0
原创粉丝点击