美文网首页
四大引用

四大引用

作者: Lucksheep | 来源:发表于2020-06-16 15:28 被阅读0次

    一、 GC回收日志打印基本设置

    启动设置:

    -verbose:gc             //开启gc日志    
    -XX:+PrintGCDetails     //打印gc详情
    -XX:+PrintGCDateStamps  //打印gc时间戳
    -XX:+PrintHeapAtGC      //在进行GC的前后打印出堆的信息
    -Xloggc:gc.log          //日志输出名称
    -Xms3M                  //初始内存
    -Xmx4M                  //最大可用内存
    

    日志各个字段含义简述:

    2019-03-28  日期
    
    10:02:40.628+0800: 执行回收时间 
    
    0.080: 启动JVM到该次垃圾回收时间
    
    GC/Full GC :垃圾回收类型
    
    Allocation Failure : 垃圾回收的原因,此时为Eden区空间不足
    
    ParNew/CMS/Metaspace : 垃圾回收区域 新生代/老年代/元空间
    
    [PSYoungGen: 510K->504K(1024K)]  清理前占用 -> 清理后占用(新生代内存总大小)
    
    PS代表新生代的是使用的是Parallel Scavenge收集器垃圾收集器
    
    [ParOldGen]  Par代表老年代使用的是Parallel old垃圾回收器
    
    0.0006474 secs 表示回收的时间
    
    510K->536K(3584K)  整个堆内存清理前占用 ——> 清理后占用(对内存总大小)
    
    [Metaspace: 3304K->3304K(1056768K)] 这部分表示的是方法区的内存大小变化
    
    [Times: user=0.00 sys=0.00, real=0.00 secs]
    
    user 代表的是用户状态耗时0.00,
    
    sys 系统状态耗时0.00,
    
    real CPU实际的执行时间为0.00
    
    Ergonomics:HotSpot自动选择和调优引发的FullGC
    

    二、了解其概念及其区别

    我们在实际开发中,往往会执行一些方法加载一些参数值到内存中,后期随着业务的发展、方法的执行等等大量的数据存在内存中,结果导致内存占用过多而产生的溢出异常,因此我们期望自己的放到内存中的对象也具有生命周期(如:内存不足时,jvm会自动回收掉某些对象从而避免OOM的错误)这个时候就需要用到软引用和弱引用

    从JDK 1.2之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱。

    强引用:

    Java中默认声明的就是强引用,如:Object object=new Object();只要强引用还存在,JVM必定不会回收掉被引用的对象,即使在内存不足的情况下,JVM宁愿抛出OOM异常,也不会回收这种对象;
    如下代码:

    public class StronglyReference {
        public static void main(String[] args) {
            Object object=new Object();
            System.gc();
            System.out.println("GC回收后:"+object);
            try {
                byte[] buff2 = new byte[1024 * 1024 * 4];
            } catch (OutOfMemoryError e) {
                //为了程序继续执行下去,捕获异常,如果不捕获会抛出异常且回收
                System.out.println("创建对象发生OOM异常,哥,你宁愿发生异常也不回收掉哎,何必呢!");
            }
            //将引用设置为null,jvm在适当的时候会回收该对象
            object=null;
            System.out.println("设置为空后:"+object);
        }
    }
    

    结果产出:

    GC回收后:java.lang.Object@12a3a380
    创建对象发生OOM异常,哥,你宁愿发生异常也不回收掉哎,何必呢!
    设置为空后:null
    

    经过上面的demo实例,然后分析GC日志,我们得出下面的问题和答案;
    问:那么在使用强引用的时候,JVM是么时候会触发回收?
    答:

    1. 线程结束时回收;
    2. 当 softReference 设置为空的时候,会被回收;

    软引用(SoftReference):

    官方解释:软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。
    查看一下事例:

    public class SoftReferences {
        public static void main(String[] args) {
            List<SoftReference> list=new ArrayList<SoftReference>();
            for (int i=0;i<10;i++){
                byte[] softBuff = new byte[1024 * 1024 * 1];
                SoftReference sr = new SoftReference(softBuff);
                list.add(sr);
            }
            for (SoftReference sr:list) {
                System.out.println(sr.get());
            }
        }
    }
    

    返回结果:

    null
    null
    null
    null
    null
    null
    null
    null
    null
    [B@12a3a380
    

    在GC日志中我们可以看到一个关键字出现:Ergonomics(解释上面有说明)
    由此我们可以得出一个结论与解释一致
    (还有一种情况,当软引用设置为空时也会被回收)
    事例代码:

    public static void main(String[] args) {
        byte[] softBuff1 = new byte[1024 * 1024 * 2];
        SoftReference sr1 = new SoftReference(softBuff1);
        sr1=null;
        System.out.println(sr1.get());
    }
    

    result:

    Exception in thread "main" java.lang.NullPointerException
    

    由此可见,设置为空时,对象也被回收了;
    弱引用(WeakReference):
    这个其实挺简单的:只要JVM进行垃圾回收,无论内存是否充足,都会回收弱引用关联的对象,在 java 中,用 java.lang.ref.WeakReference 类来表示
    事例:

    public class WeakReferences {
        public static void main(String[] args) {
            try {
                WeakReference w1=new WeakReference(new Object());
                System.out.println(w1.get());
                System.gc();
                System.out.println(w1.get());
            } catch (NullPointerException e) {
                System.out.println("W1 会抛出空指针异常,表示被回收");
            }
        }
    }
    

    result:

    java.lang.Object@12a3a380
    null
    

    虚引用(PhantomReference):

    官方解释:如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用PhantomReference 类来表示;我们看下源码
    源码:

    public class PhantomReference<T> extends Reference<T> {
        
        public T get() {
            return null;
        }
        
        public PhantomReference(T referent, ReferenceQueue<? super T> q) {
            super(referent, q);
        }
    }
    

    通过源码我们可以清晰的了解到,它只有一个构造函数和一个 get()方法,而且 get() 方法是固定的返回的null,这样玩们就知道了,我们永远无法单独的通过虚引用来获取对象,虚引用也必须要和ReferenceQueue引用队列一起使用。这是是它和软引用、弱引用最直观的局别;

    引用队列(ReferenceQueue)

    官方解释:引用队列可以与软引用、弱引用以及虚引用一起配合使用,当垃圾回收器准备回收一个对象时,如果发现它还有引用,那么就会在回收对象之前,把这个引用加入到与之关联的引用队列中去。程序可以通过判断引用队列中是否已经加入了引用,来判断被引用的对象是否将要被垃圾回收,这样就可以在对象被回收之前采取一些必要的措施。

    与软引用、弱引用不同,虚引用必须和引用队列一起使用(上面有提到过)。

    public class PhantomReferences {
        public static void main(String[] args) throws InterruptedException {
            ReferenceQueue queue=new ReferenceQueue();
            Object phantomObject=new Object();
            PhantomReference phantomReference=new PhantomReference (phantomObject,queue);
            System.out.println(phantomReference.get());
            phantomObject=null;
            System.gc();
            System.out.println(phantomReference.get());
            Thread.sleep(200);
            System.out.println(queue.poll());
        }
    }
    

    result:

    null
    null
    java.lang.ref.PhantomReference@12a3a380
    

    问:为什么会有对象输出呢?
    答:我们已经创建引用队列,并且在引用队列里面创建了对象phantomObject,当GC回收的时候发现虚引用,会将这个对象丢到队列里面,而现在插入队列的时候发现已经有了这个对象,会失败,然后会回收掉这个对象并且返回,所以会有返回对象

    相关文章

      网友评论

          本文标题:四大引用

          本文链接:https://www.haomeiwen.com/subject/kxhrxktx.html