JVM 引用

简介: JVM中所有的引用类型,都是抽象类java.lang.ref.Reference的子类,这个类的主要方法为get()方法。

JVM中所有的引用类型,都是抽象类java.lang.ref.Reference的子类,这个类的主要方法为get()方法:

public abstract class Reference<T> {}

除了虚引用(因为get永远返回null),如果对象还没有被销毁,都可以通过get方法获取原有对象。这意味着,利用软引用和弱引用,我们可以将访问到的对象,重新指向强引用,也就是人为的改变了对象的可达性状态。

public T get() {
    return this.referent;
}

强引用:

强引用(Strong References)就是直接new一个普通对象,表示一种比较强的引用关系,只要还有强引用对象指向一个对象,那么表示这个对象还活着,垃圾收集器宁可抛出OOM异常,也不会回收这个对象。

public class StrongReferenceDemo {
    public static void main(String[] args) throws IOException {
        // 创建一个User对象,此时u就是一个强引用:
        User u = new User();
        // 由于强引用,u引用不会被回收:
        System.gc();
        System.out.println(u);
        // u这个引用指向为空,这时new出来,在堆空间的User实例,没有引用指向
        // 在进行GC时,这个实例就会被GC回收
        u = null;
        // 手动出发GC,这时User实例就会被GC回收,并触发finalize方法:
        System.gc();
        System.in.read();
    }
}
public class User {
    // 重写finalize方法,在触发GC时调用(这个方法调用时,该对象将被进行回收)
    @Override
    protected void finalize() throws Throwable {
        System.out.println("call User finalize() method");
    }
}

上面的User对象重写了父类Objectfinalize(),在GC准备释放对象所占用的内存空间之前,它将首先调用finalize()方法。

Java中,由于GC的自动回收机制,因而并不能保证finalize方法会被及时地执行(垃圾对象的回收时机具有不确定性),也不能保证它们会被执行(程序由始至终都未触发垃圾回收),所以finalize不推荐使用,这里只是为了演示垃圾回收的过程。

另外finalize()最多只会被调用一次,也就是只能利用finalize()为对象续命一次。

弱引用:

软引用用于存储一些可有可无的东西,例如缓存,当系统内存充足时,这些对象不会被回收,当系统内存不足时也是GC时才会回收这些对象,如果回收完这些对象后内存还是不足,就会抛出OOM异常。

// vm args: -Xmx36m -XX:+PrintGCDetails
public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        SoftReference<User> softReference = new SoftReference<>(new User()); // 软引用
        System.out.println(softReference.get());
        System.gc(); 
        // wait gc thread run
        TimeUnit.SECONDS.sleep(3); 
        // 由于堆内存空间充足,这时候软引用不会被GC回收
        // User对象不会被回收
        System.out.println(softReference.get()); 
        // 分配一个大对象使得堆空间不足,软引用对象会在OOM之前先被回收
        byte[] bytes = new byte[1024 * 1024 * 1024]; 
        System.out.println(softReference.get());
    }
}

在上面的例子中,第一次发生gc时,User对象不会被回收,第二次发生gc时由于堆空间不足,会先回收软引用的对象,回收完了还是空间不足,最后抛出OOM异常。

软引用:

弱引用(WeakReference)并不能使对象豁免垃圾回收,仅仅是提供一种访问在弱引用状态下对象的途径。只要发生gc,弱引用对象就会被回收。ThreadLocal中就使用了WeakReference来避免内存泄漏。

public class WeakReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        WeakReference<User> weakReference = new WeakReference<>(new User());
        System.out.println(weakReference.get());
        System.gc();
        TimeUnit.SECONDS.sleep(3); // wait gc thread run
        System.out.println(weakReference.get()); // null
    }
}

上面的例子只要发生gcUser对象就会被垃圾收集器回收。

虚引用:

虚引用必须和引用队列(ReferenceQueue)联合使用。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象之前,把这个虚引用加入到与之关联的引用队列中。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

// vm args: -Xms4m -XX:+PrintGC
public class PhantomReferenceDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 引用队列
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>(); 
        // 虚引用
        PhantomReference<User> phantomReference = new PhantomReference<>(new User(), referenceQueue); 
        // 虚引用在调用get时返回值一定为null:
        System.out.println(phantomReference.get()); // null
      // 创建一个子线程:
        new Thread(() -> {
            while (true) {
                Reference<? extends User> poll = referenceQueue.poll();
                if (poll != null) {
                    System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
                    System.out.println("--- 回收对象 ---- " + poll.get()); // null
                }
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        System.gc();
        System.in.read();
    }
    private static class User {
        private int[] bytes = new int[1024 * 1024 * 5];
        @Override
        protected void finalize() throws Throwable {
            System.out.println("call User finalize() method");
        }
    }
}

基于虚引用,有一个更加优雅的实现方式,那就是Cleaner,可以用来替代Object类的finalizer方法,在DirectByteBuffer中用来回收堆外内存。

相关文章
|
6月前
|
缓存 Java API
JVM 四种引用和使用场景
在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,Java 4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用。
65 0
|
6月前
|
存储 缓存 算法
深入浅出JVM(十四)之内存溢出、泄漏与引用
深入浅出JVM(十四)之内存溢出、泄漏与引用
|
存储 缓存 算法
GC面临的困境,JVM是如何解决跨代引用的?
前面我们讲了可达性分析和根节点枚举,介绍完了GC的前置工作,下面开始讲GC的工作过程。
160 0
GC面临的困境,JVM是如何解决跨代引用的?
|
存储 算法 Java
JVM 收集算法 垃圾收集器 元空间 引用
JVM 收集算法 垃圾收集器 元空间 引用
106 0
|
存储 缓存 算法
jvm之4种引用的解读
jvm之4种引用的解读
|
缓存 算法 Java
《深入理解Java虚拟机》读书笔记(四)--GC的回收条件及Java对象的引用
《深入理解Java虚拟机》读书笔记(四)--GC的回收条件及Java对象的引用
234 0
|
Java
JVM垃圾回收——五种引用
如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时,软引用本身不会被清理
144 0
JVM垃圾回收——五种引用
玩转JVM中的对象及引用:从创建到引用到分配和优化策略
类加载检查 当Java虚拟机遇到一条new指令的时候,它会先去运行时常量池中寻找new的类的符号引用,并且检查这个符号引用所代表的类是否已经被加载、解析、初始化过。如果没有即需要进行相应的类加载过程。
|
算法 安全 Java
|
存储 缓存 监控
【JVM深度解析】JVM中的对象及引用
本文通过对象的创建步骤中的检查加载->分配内存->内存空间初始化->设置->对象初始化,对象的内存布局,什么是垃圾的两种算法以及四种引用,讲述JVM中对象及引用。
【JVM深度解析】JVM中的对象及引用