• JVM四种引用级别


    强引用

    Object obj = new Object();

    强引用对象什么时候失效?

    1. 生命周期结束(作用域失效)

      public void method(){
          Object obj = new Object();
      }
      //当方法执行完毕之后,强引用指向的引用对象[new Object()]就会等待被GC回收
      
    2. 引用被置为null

      obj = null;
      

    除了以上两种情况以外,其他任何时候的GC都不会回收强引用对象。

    软引用

    根据内存情况回收:如果内存充足,GC不会随便回收软引用对象。如果内存不足,GC就会主动回收软引用对象。

    package qx.leizige.ref;
    
    import java.lang.ref.SoftReference;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Objects;
    
    //软引用对象
    class Student {
        private String name;
    }
    
    /**
     * @author leizige
     * 2022/01/29
     */
    public class SoftReferenceDemo {
    
        public static void main(String[] args) throws InterruptedException {
            //softRef --> softObject
            SoftReference<Student> softRef = new SoftReference<>(new Student());
    
            //开启新的线程监听是否有软引用对象被回收
            new Thread(() -> {
                while (true) {
                    if (Objects.isNull(softRef.get())) {
                        System.out.println("软引用对象[student]已经被回收......");
                        System.exit(0);
                    }
                }
            }, "softRef Listener").start();
    
    
            //不断得往集合中存放数据,模拟内存不足
            List<byte[]> byteList = new ArrayList<>();
            while (true) {
                if (Objects.nonNull(softRef.get())) {
                    byteList.add(new byte[1024 * 1024]);    //每次add 1M 数据
                }
            }
        }
    }
    

    弱引用

    回收时机:只要GC执行,就会将弱引用对象进行回收。

    package qx.leizige.ref;
    
    import java.lang.ref.WeakReference;
    
    /**
     * @author leizige
     * 2022/01/29
     */
    public class WeakReferenceDemo {
    
        public static void main(String[] args) throws InterruptedException {
    
    
            WeakReference<Object> weakRef = new WeakReference<>(new Object());
    
            System.out.println(weakRef.get() == null ? "weakRef 已被回收" : "weakRef 没被回收");
    
            System.gc();    //建议GC执行一次回收
            Thread.sleep(1000);
    
            System.out.println(weakRef.get() == null ? "weakRef 已被回收" : "weakRef 没被回收");
    
        }
    }
    

    虚引用

    是否使用虚引用,和引用对象本身没有任何关系。无法通过虚引用来获取对象本身,PhantomReferenceget方法总是返回 null。

    虚引用一般不会单独使用,而是和引用队列(ReferenceQueue)一起使用。

    当GC回收一个对象时,发现该对象还有一个虚引用,就会将该对象放进引用队列中,之后(虚引用出队之后)再去回收该对象。

    GC --> 如果有虚引用 --> 虚引用入队 --> 虚引用出队 --> 回收对象。

    package qx.leizige.ref;
    
    
    import java.lang.ref.PhantomReference;
    import java.lang.ref.ReferenceQueue;
    
    class Person {
        private String name;
    
        public Person(String name) {
            this.name = name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    /**
     * @author leizige
     * 2022/01/29
     */
    public class PhantomReferenceDemo {
    
        public static void main(String[] args) throws InterruptedException {
    
            Person person = new Person("张三");
    
            //开启一个引用队列
            ReferenceQueue<Person> refQueue = new ReferenceQueue<>();
    
            //虚引用必须和队列一起使用
            PhantomReference<Person> phantomRef = new PhantomReference<>(person, refQueue);
    
            System.out.println("phantomRef = " + phantomRef.get());
            System.out.println("refQueue = " + refQueue.poll());
    
    
            person = null;
            System.gc();
            Thread.sleep(500);
    
            System.out.println("person = " + person);
            System.out.println("phantomRef = " + phantomRef.get());
            //回收之后,回收的对象到了引用队列里面
            System.out.println("refQueue = " + refQueue.poll());
    
        }
    }
    

    特殊情况:如果重写了 Person 对象的 finalize 方法,那么JVM会延迟入队。可能在第二次,也可能在第三次GC时入队。

    package qx.leizige.ref;
    
    
    import java.lang.ref.PhantomReference;
    import java.lang.ref.ReferenceQueue;
    
    class Person {
        private String name;
    
        public Person(String name) {
            this.name = name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    '}';
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("即将回收该对象.....");
        }
    }
    
    /**
     * @author leizige
     * 2022/01/29
     */
    public class PhantomReferenceDemo {
    
        public static void main(String[] args) throws InterruptedException {
    
            Person person = new Person("张三");
    
            //开启一个引用队列
            ReferenceQueue<Person> refQueue = new ReferenceQueue<>();
    
            //虚引用必须和队列一起使用
            PhantomReference<Person> phantomRef = new PhantomReference<>(person, refQueue);
    
            System.out.println("phantomRef = " + phantomRef.get());
            System.out.println("refQueue = " + refQueue.poll());
    
    
            person = null;
            System.gc();
    
            System.out.println("person = " + person);
            System.out.println("phantomRef = " + phantomRef.get());
            //回收之后,回收的对象到了引用队列里面
            System.out.println("refQueue = " + refQueue.poll());
    
            person = null;
            System.gc();
    
            System.out.println("person1 = " + person);
            System.out.println("phantomRef = " + phantomRef.get());
            //回收之后,回收的对象到了引用队列里面
            System.out.println("refQueue1 = " + refQueue.poll());
    
    
            person = null;
            System.gc();
    
            System.out.println("person = " + person);
            System.out.println("phantomRef = " + phantomRef.get());
            //回收之后,回收的对象到了引用队列里面
            System.out.println("refQueue2 = " + refQueue.poll());
    
    
            person = null;
            System.gc();
    
            System.out.println("person = " + person);
            System.out.println("phantomRef = " + phantomRef.get());
            //回收之后,回收的对象到了引用队列里面
            System.out.println("refQueue3 = " + refQueue.poll());
    
            person = null;
            System.gc();
    
            System.out.println("person = " + person);
            System.out.println("phantomRef = " + phantomRef.get());
            //回收之后,回收的对象到了引用队列里面
            System.out.println("refQueue4 = " + refQueue.poll());
    
        }
    }
    
  • 相关阅读:
    Sentinel Dashboard(基于1.8.1)流控规则持久化到Nacos——涉及部分Sentinel Dashboard源码改造
    测试平台MeterSphere源码入门
    Java:利用BigDecimal类巧妙处理Double类型精度丢失
    SpringBoot整合任务调度框架Quartz及持久化配置
    任务调度框架Quartz快速入门!
    Kafka超详细学习笔记【概念理解,安装配置】
    Windows环境下Zookeeper安装配置
    SpringData JPA利用Specification多条件查询
    SpringBoot事件监听机制及观察者模式/发布订阅模式
    详解Java中的IO输入输出流!
  • 原文地址:https://www.cnblogs.com/leizzige/p/15855363.html
Copyright © 2020-2023  润新知