Java对象引用

426 阅读1分钟

Java引用类型有强、软、弱、虚引用。分别有不同的作用。

强引用

基本日常开发过程中GC Root能够追溯到的都是强引用。比如局部声明的局部变量。触发GC时触发GC Root不可达,否则不会收集强引用的对象。

软引用

Java中声明一个占用2MB内存的软引用。

SoftReference<byte[]> softReference = new SoftReference<>(new byte[(1024*1024*2)]);

软引用回收的条件(或关系):

  1. GC Root不可达。
  2. 当发生GC内存还是不足时,会回收软引用的对象。

示例代码:

//4mb
SoftReference<byte[]> softReference = new SoftReference<>(new byte[(1024*1024*2)]);
@Test
public void softReference() {
    //-Xmx5m
    System.out.println("软引用"+softReference.get());
    List<byte[]> a = new ArrayList<>();
    // 分配2mb,由于堆空间不足,会进行Full GC清理,清理完成后还是发现不足,会清理软引用,还是不足会抛出OOM
    // 此处会清理软引用
    for (int i = 0; i<=1; i++) {
        a.add(new byte[1024*1024]);// 1mb
    }
    System.out.println("软引用end "+softReference.get());
}

image.png

软引用比较适合缓存场景,使用软引用可以无需考虑内存不足情况下的淘汰策略,JVM GC时会自动清理,比较方便。使用时需要注意,所有的软引用都会被清理,JVM清理一个软引用后,不会根据内存是否足够再次判断是否清理,而是直接清理所有软引用。

虚引用

虚引用可以用来监听对象是否已经被回收,可以替代java.lang.Object#finalize方法。我们知道java.lang.Object#finalize方法的执行是在判断一个对象是垃圾之后,在清楚对象之前执行,单线程执行。所以java.lang.Object#finalize的使用会延后内存空间的释放。而使用虚引用是在对象内存清除后触发,可以自己控制线程。

示例代码:

@Test
public void phantomReference() throws InterruptedException {
    System.out.println("虚引用");
    ReferenceQueue<Object> rq = new ReferenceQueue<>();
    Object o1 = new Object();
    PhantomReference<Object> objectPhantomReference1 = new PhantomReference<>(o1, rq);
    System.out.println(objectPhantomReference1);
    o1 = null;
    System.gc();
    TimeUnit.SECONDS.sleep(1);
    System.out.println(rq.poll());// 返回的是虚引用对象 不是 o1
    System.out.println("幻象引用end");
}

image.png

虚引用主要使用在监听对象是否被清除进行资源释放的场景中。