java中的强引用(Strong reference),软引用(SoftReference),弱引用(WeakReference),虚引用(PhantomReference)

之前在看深入理解Java虚拟机一书中第一次接触相关名词,但是并不理解,只知道Object obj = new Object()类似这种操作的时候,obj就是强引用。强引用不会被gc回收直到gc roots不可达时。而对其他三个名词并不清楚,因为并没有被真正使用过。通过查看软引用,弱引用和虚引用的源码,可以看出这三个类都是继承自Reference。

技术分享图片

一 、概念


1.1软引用(SoftReference)
我理解的软引用的意思是,即使引用对象没有被使用了,gc也不会马上回收,而是只有当堆内存空间不够时才会回收。一般用于缓存对象,因为希望在内存中保留的越久越好。软引用所指向的引用对象停留在堆内存的时间由当前可用堆内存的大小来控制。软引用中有两个字段:clock和timestamp。clock记录初始化或gc的时间,而get引用对象时会更新timestamp与clock时间一致。如果clock和timestamp的间隔时间超过maxInternal,则认为该引用对象很久没有使用是时候回收该引用对象,否则会继续存在。maxInternal的时间有当前可用堆内存大小决定。
1.2弱引用 (WeakReference)
弱引用的意思是如果引用对象gc roots不可达,gc就会进行回收。get操作可能返回引用对象(如果没有gc),否则返回空。
1.3虚引用 (PhantomReference)
虚引用不能返回引用对象,因为get操作返回的一直是null,感觉就是一个虚无缥缈的引用,所以这是称之为虚引用的原因吧。


二 、实例

public class TestObj {
    private final int _1M=1024*1024;
    private String name;
    private String field;
    private byte[] byteArr=new byte[_1M];

    public TestObj(String name,String field){
        this.name=name;
        this.field=field;
    }

    public String toString(){
        return "TestObj{"+"name='"+name+'\''+",field='"+field+'\''+'}';
    }
}

构造一个对象TestObj,它的大小为1M多一点。
JVM参数设置:
-Xms16M 初始堆大小
-Xmx16M 堆的最大值
-Xmn8M 年轻代的大小
-XX:+PrintGCDetails
-XX:SurvivorRatio=8   eden:survivor的比例为8:1

第一种情况:
 

/**
 * -Xms8M 初始堆大小
 * -Xmx8M  堆最大
 * -Xmn4M  年轻代大小
 * -XX:+PrintGCDetails 打印gc日志
 * -XX:SuvivorRatio=8 eden:suvivor=8:1
 */
public class TestJavaReference {
    public static void main(String[] args){
        TestObj testObjForSoft = new TestObj("softReference","value");
        TestObj testObjForWeak = new TestObj("weakReference","value");
        TestObj testObjForPhantom = new TestObj("phantomReference","value");
        SoftReference<TestObj> softReference = new SoftReference<TestObj>(testObjForSoft);
        WeakReference<TestObj> weakReference = new WeakReference<TestObj>(testObjForWeak);
        ReferenceQueue<TestObj> phantomReferenceQueue = new ReferenceQueue<TestObj>();
        PhantomReference<TestObj> phantomReference = new PhantomReference<TestObj>(testObjForPhantom,phantomReferenceQueue);
        System.gc();

        testObjForSoft=null;
        testObjForWeak=null;
        testObjForPhantom=null;

        TestObj[] cost = new TestObj[3];
        //给引用类型数组元素赋值,消耗3M多点的内存空间
        cost[0]=new TestObj("cost1","cost");
        cost[1]=new TestObj("cost2","cost");
        cost[2]=new TestObj("cost3","cost");
        //直接实例化基础类型数组,消耗内存
        //消耗4M多点的堆空间,触发对weakReference,phantomReference实例的回收
        //byte[] cost4= new byte[1024*1024*4];
        //再消耗4M多点的堆空间,触发softReference实例的回收
        //byte[] cost5= new byte[1024*1024*4];

        TestObj softObj = softReference.get();
        if(softObj!=null){
            System.out.println("softReference reference object is still alive! "+softObj.toString());
        }else{
            System.out.println("softReference reference object is not alive!");
        }

        TestObj weakObj = weakReference.get();
        if(weakObj!=null){
            System.out.println("weakReference reference object is still alive! "+weakObj.toString());
        }else{
            System.out.println("weakReference reference object is not alive!");
        }

        Reference<TestObj> phantomReferenceFromQueue = (Reference<TestObj>)phantomReferenceQueue.poll();
        if(phantomReferenceFromQueue!=null){
            TestObj phantomObj = phantomReferenceFromQueue.get();
            if(phantomObj!=null){
                System.out.println("phantomReference reference object is still alive! "+phantomObj.toString());
            }else{
                System.out.println("phantomReference reference object is not alive!");
            }
        }

    }
}

gc过程:
 

[GC (System.gc()) [PSYoungGen: 6875K->510K(7680K)] 6875K->4334K(15872K), 0.0042607 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 510K->0K(7680K)] [ParOldGen: 3824K->4298K(8192K)] 4334K->4298K(15872K), [Metaspace: 3315K->3315K(1056768K)], 0.0127617 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
softReference reference object is still alive! TestObj{name='softReference',field='value'}
weakReference reference object is still alive! TestObj{name='weakReference',field='value'}
Heap
 PSYoungGen      total 7680K, used 3329K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 46% used [0x00000000ff800000,0x00000000ffb40500,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 8192K, used 4298K [0x00000000ff000000, 0x00000000ff800000, 0x00000000ff800000)
  object space 8192K, 52% used [0x00000000ff000000,0x00000000ff432aa8,0x00000000ff800000)
 Metaspace       used 3379K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 361K, capacity 388K, committed 512K, reserved 1048576K

分析:
调用System.gc的时候对象强引用还存在,所以三个引用对象会进入到老年代。此时老年代保存softReference,weakReference,phantomReference这三个TestObj实例,eden中保存cost数组中三个TestObj实例。


第二种情况:
 

/**
 * -Xms8M 初始堆大小
 * -Xmx8M  堆最大
 * -Xmn4M  年轻代大小
 * -XX:+PrintGCDetails 打印gc日志
 * -XX:SuvivorRatio=8 eden:suvivor=8:1
 */
public class TestJavaReference {
    public static void main(String[] args){
        TestObj testObjForSoft = new TestObj("softReference","value");
        TestObj testObjForWeak = new TestObj("weakReference","value");
        TestObj testObjForPhantom = new TestObj("phantomReference","value");
        SoftReference<TestObj> softReference = new SoftReference<TestObj>(testObjForSoft);
        WeakReference<TestObj> weakReference = new WeakReference<TestObj>(testObjForWeak);
        ReferenceQueue<TestObj> phantomReferenceQueue = new ReferenceQueue<TestObj>();
        PhantomReference<TestObj> phantomReference = new PhantomReference<TestObj>(testObjForPhantom,phantomReferenceQueue);
        System.gc();

        testObjForSoft=null;
        testObjForWeak=null;
        testObjForPhantom=null;

        TestObj[] cost = new TestObj[3];
        //给引用类型数组元素赋值,消耗3M多点的内存空间
        cost[0]=new TestObj("cost1","cost");
        cost[1]=new TestObj("cost2","cost");
        cost[2]=new TestObj("cost3","cost");
        //直接实例化基础类型数组,消耗内存
        //消耗4M多点的堆空间,触发对weakReference,phantomReference实例的回收
        byte[] cost4= new byte[1024*1024*4];
        //再消耗4M多点的堆空间,触发softReference实例的回收
        //byte[] cost5= new byte[1024*1024*4];

        TestObj softObj = softReference.get();
        if(softObj!=null){
            System.out.println("softReference reference object is still alive! "+softObj.toString());
        }else{
            System.out.println("softReference reference object is not alive!");
        }

        TestObj weakObj = weakReference.get();
        if(weakObj!=null){
            System.out.println("weakReference reference object is still alive! "+weakObj.toString());
        }else{
            System.out.println("weakReference reference object is not alive!");
        }

        Reference<TestObj> phantomReferenceFromQueue = (Reference<TestObj>)phantomReferenceQueue.poll();
        if(phantomReferenceFromQueue!=null){
            TestObj phantomObj = phantomReferenceFromQueue.get();
            if(phantomObj!=null){
                System.out.println("phantomReference reference object is still alive! "+phantomObj.toString());
            }else{
                System.out.println("phantomReference reference object is not alive!");
            }
        }

    }
}

gc过程:
 

[GC (System.gc()) [PSYoungGen: 6577K->502K(7680K)] 6577K->4354K(15872K), 0.0289419 secs] [Times: user=0.02 sys=0.00, real=0.03 secs] 
[Full GC (System.gc()) [PSYoungGen: 502K->0K(7680K)] [ParOldGen: 3852K->4291K(8192K)] 4354K->4291K(15872K), [Metaspace: 3208K->3208K(1056768K)], 0.0242277 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 3282K->96K(7680K)] 7573K->7459K(15872K), 0.0013541 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 96K->0K(7680K)] [ParOldGen: 7363K->6186K(8192K)] 7459K->6186K(15872K), [Metaspace: 3244K->3244K(1056768K)], 0.0123107 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
softReference reference object is still alive! TestObj{name='softReference',field='value'}
weakReference reference object is not alive!
phantomReference reference object is not alive!
Heap
 PSYoungGen      total 7680K, used 4593K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 64% used [0x00000000ff800000,0x00000000ffc7c5e0,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 8192K, used 6186K [0x00000000ff000000, 0x00000000ff800000, 0x00000000ff800000)
  object space 8192K, 75% used [0x00000000ff000000,0x00000000ff60a8b8,0x00000000ff800000)
 Metaspace       used 3273K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 353K, capacity 388K, committed 512K, reserved 1048576K

分析:
由于此时年轻代不能再分配一个4M多的空间,而老年代也放不下,所以会发生full gc, 弱引用和虚引用的对象会被回收,而软引用还存在。此时老年代保存softReference这个TestObj实例,以及cost数组中三个TestObj实例,eden中保存cost4这个byte数组。


第三种情况:
 

/**
 * -Xms8M 初始堆大小
 * -Xmx8M  堆最大
 * -Xmn4M  年轻代大小
 * -XX:+PrintGCDetails 打印gc日志
 * -XX:SuvivorRatio=8 eden:suvivor=8:1
 */
public class TestJavaReference {
    public static void main(String[] args){
        TestObj testObjForSoft = new TestObj("softReference","value");
        TestObj testObjForWeak = new TestObj("weakReference","value");
        TestObj testObjForPhantom = new TestObj("phantomReference","value");
        SoftReference<TestObj> softReference = new SoftReference<TestObj>(testObjForSoft);
        WeakReference<TestObj> weakReference = new WeakReference<TestObj>(testObjForWeak);
        ReferenceQueue<TestObj> phantomReferenceQueue = new ReferenceQueue<TestObj>();
        PhantomReference<TestObj> phantomReference = new PhantomReference<TestObj>(testObjForPhantom,phantomReferenceQueue);
        System.gc();

        testObjForSoft=null;
        testObjForWeak=null;
        testObjForPhantom=null;

        TestObj[] cost = new TestObj[3];
        //给引用类型数组元素赋值,消耗3M多点的内存空间
        cost[0]=new TestObj("cost1","cost");
        cost[1]=new TestObj("cost2","cost");
        cost[2]=new TestObj("cost3","cost");
        //直接实例化基础类型数组,消耗内存
        //消耗4M多点的堆空间,触发对weakReference,phantomReference实例的回收
        byte[] cost4= new byte[1024*1024*4];
        //再消耗4M多点的堆空间,触发softReference实例的回收,这里要分步初始化,防止出现内存溢出
        byte[] cost5= new byte[1024*1024*2];
        byte[] cost6= new byte[1024*1024];
        byte[] cost7= new byte[1024*1024];

        TestObj softObj = softReference.get();
        if(softObj!=null){
            System.out.println("softReference reference object is still alive! "+softObj.toString());
        }else{
            System.out.println("softReference reference object is not alive!");
        }

        TestObj weakObj = weakReference.get();
        if(weakObj!=null){
            System.out.println("weakReference reference object is still alive! "+weakObj.toString());
        }else{
            System.out.println("weakReference reference object is not alive!");
        }

        Reference<TestObj> phantomReferenceFromQueue = (Reference<TestObj>)phantomReferenceQueue.poll();
        if(phantomReferenceFromQueue!=null){
            TestObj phantomObj = phantomReferenceFromQueue.get();
            if(phantomObj!=null){
                System.out.println("phantomReference reference object is still alive! "+phantomObj.toString());
            }else{
                System.out.println("phantomReference reference object is not alive!");
            }
        }

    }
}

gc过程:
 

[GC (System.gc()) [PSYoungGen: 6875K->502K(7680K)] 6875K->4386K(15872K), 0.0029011 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 502K->0K(7680K)] [ParOldGen: 3884K->4327K(8192K)] 4386K->4327K(15872K), [Metaspace: 3369K->3369K(1056768K)], 0.0102190 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 3207K->96K(7680K)] 7535K->7495K(15872K), 0.0029352 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 96K->0K(7680K)] [ParOldGen: 7399K->6375K(8192K)] 7495K->6375K(15872K), [Metaspace: 3370K->3370K(1056768K)], 0.0066042 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 6279K->6144K(7680K)] [ParOldGen: 6375K->6219K(8192K)] 12655K->12363K(15872K), [Metaspace: 3371K->3371K(1056768K)], 0.0173394 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
[Full GC (Ergonomics) [PSYoungGen: 6215K->6144K(7680K)] [ParOldGen: 7243K->7243K(8192K)] 13458K->13387K(15872K), [Metaspace: 3371K->3371K(1056768K)], 0.0034531 secs] [Times: user=0.05 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 6144K->6144K(7680K)] [ParOldGen: 7243K->6190K(8192K)] 13387K->12334K(15872K), [Metaspace: 3371K->3371K(1056768K)], 0.0068943 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
softReference reference object is not alive!
weakReference reference object is not alive!
phantomReference reference object is not alive!
Heap
 PSYoungGen      total 7680K, used 6408K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 89% used [0x00000000ff800000,0x00000000ffe42340,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 8192K, used 7214K [0x00000000ff000000, 0x00000000ff800000, 0x00000000ff800000)
  object space 8192K, 88% used [0x00000000ff000000,0x00000000ff70b828,0x00000000ff800000)
 Metaspace       used 3379K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 361K, capacity 388K, committed 512K, reserved 1048576K

分析:
由于eden:7168k的空间已经被使用了89%,所以不能在放下一个1M的对象,而且老年代8192k的空间也已经消耗了88%,不能再放下一个1M的对象。所以只能full gc(当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC。full gc 和 young gc关系参考:https://www.zhihu.com/question/41922036/answer/93079526),所以软引用所指对象被最终收回。果然软引用存活时间够长,只要堆内存足够。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值