【Java】强引用、弱引用、软引用、虚引用

1 引用的作用

引用是用来指向一块内存区域,程序通过引用来获取对象信息,它关系着对象的生命周期。

2 为何需要多种引用类型?

默认为强引用,只要对象被任意强引用所指向,则对象不会被垃圾收集器清理。

那有啥问题呢?

有时有些对象的原始引用被指向为NULL,我们希望JVM可以清理掉这个无用对象,但此刻该对象可能被其他强引用所包含,那总不能一个个将所有引用指向为NULL。此刻就需要其他引用的支持。比如如果其他对象包含的引用是该对象的弱引用,那么JVM会在清理时忽略它的存在。

3 强引用、弱引用、软引用、虚引用

3.1 强引用

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfM moryError错误,使程序异常终止,也不会靠回收具有强引用 对象来解决内存不足的问题。

应用:基本上new出来的都是强引用。除了下面三个家伙

3.2 软引用

 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。

应用:实现内存敏感的高速缓存。

3.3 虚引用

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到ReferenceQueue中。

应用:

JDK中直接内存的回收就用到虚引用,JAVA在申请一块直接内存之后,会在堆内存分配一个对象保存这个堆外内存的引用,这个对象被垃圾收集器管理,一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作。

3.4 弱引用

如果这个对象只被弱引用关联,那么这个对象就会被垃圾回收器回收。

应用:有时某些缓存和副本创建后,如果被其他用户使用则留着,如果没有使用,那么发生GC时,则希望可以将该类对象可以被清理。如JDK中的:WeakHashMap和ThreadLocal。

举个栗子:

package jvm;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

/**
 * WeakReference测试
 * 
 * @author chaozai
 * @date 2019年3月27日
 *
 */
public class WeakReferenceTest {

    public static void main(String[] args) {
	test01();
	test02();
	test03();
	test04();
    }

    /**
     * 测试1:当对象引用被置为null,GC会将对象清理,清理后通过弱引用返回null
     */
    public static void test01() {
	print("-------------test01-------------");
	Long l = new Long(100l);
	WeakReference<Long> weakRef = new WeakReference<Long>(l);
	print(weakRef.get());
	l = null;
	print(weakRef.get());
	System.gc();
	print(weakRef.get());
	print("-------------test01-------------");
    }

    /**
     * 测试2:对象引用被存放在MAP中,当对象引用被置为null,GC不会将对象清理,清理后通过弱引用返回对象,MAP中对象存在
     */
    public static void test02() {
	print("-------------test02-------------");
	Long l = new Long(100l);
	WeakReference<Long> weakRef = new WeakReference<Long>(l);
	Map<String, Long> map = new HashMap<>();
	map.put("0", l);
	print(weakRef.get());
	l = null;
	print(weakRef.get());
	System.gc();
	print(weakRef.get());
	print(map.get("0"));
	print("-------------test02-------------");
    }

    /**
     * 测试3:对象弱引用被存放在MAP中,当对象引用被置为null,GC会将对象清理,清理后通过弱引用返回null,Map中弱引用存在,但指向对象为Null
     */
    public static void test03() {
	print("-------------test03-------------");
	Long l = new Long(100l);
	WeakReference<Long> weakRef = new WeakReference<Long>(l);
	Map<String, WeakReference<Long>> map = new HashMap<>();
	map.put("0", weakRef);
	print(weakRef.get());
	l = null;
	print(weakRef.get());
	System.gc();
	print(weakRef.get());
	print(map.get("0").get());
	print("-------------test03-------------");
    }

    /**
     * 测试4:对象弱引用被其他对象持有,当对象引用被置为null,GC会将对象清理,清理后通过弱引用返回null,持有对象中弱引用指向对象为Null,而强引用对象依然不为Null
     */
    public static void test04() {
	print("-------------test04-------------");
	Long l0 = new Long(100l);
	Long l1 = new Long(100l);
	WeakReference<Long> weakRef = new WeakReference<Long>(l0);
	WeakObj wObj = new WeakObj();
	wObj.setL(l1);
	wObj.setWl(weakRef);
	l0 = null;
	l1 = null;
	print(wObj.getL());
	print(wObj.getWl().get());
	System.gc();
	print(wObj.getL());
	print(wObj.getWl().get());
	print("-------------test04-------------");
    }

    /**
     * 通用打印方法
     * 
     * @param o
     */
    private static void print(Object o) {
	System.out.println(o);
    }

}
/**
 * 测试对象:分别包含一个弱引用和一个强引用
 * @author chaozai
 * @date 2019年3月28日
 *
 */
class WeakObj {
    WeakReference<Long> wl;
    Long l;
    public WeakReference<Long> getWl() {
        return wl;
    }
    public void setWl(WeakReference<Long> wl) {
        this.wl = wl;
    }
    public Long getL() {
        return l;
    }
    public void setL(Long l) {
        this.l = l;
    }
}

运行结果:

-------------test01-------------
100
100
[GC (System.gc()) [PSYoungGen: 1229K->728K(17920K)] 1229K->736K(58880K), 0.0021357 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 728K->0K(17920K)] [ParOldGen: 8K->584K(40960K)] 736K->584K(58880K), [Metaspace: 2665K->2665K(1056768K)], 0.0098673 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
null
-------------test01-------------
-------------test02-------------
100
100
[GC (System.gc()) [PSYoungGen: 307K->64K(17920K)] 892K->648K(58880K), 0.0141342 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 64K->0K(17920K)] [ParOldGen: 584K->584K(40960K)] 648K->584K(58880K), [Metaspace: 2665K->2665K(1056768K)], 0.0216498 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
100
100
-------------test02-------------
-------------test03-------------
100
100
[GC (System.gc()) [PSYoungGen: 307K->32K(17920K)] 891K->616K(58880K), 0.0090060 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 32K->0K(17920K)] [ParOldGen: 584K->584K(40960K)] 616K->584K(58880K), [Metaspace: 2665K->2665K(1056768K)], 0.0093501 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
null
null
-------------test03-------------
-------------test04-------------
100
100
[GC (System.gc()) [PSYoungGen: 307K->96K(17920K)] 891K->680K(58880K), 0.0007292 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 96K->0K(17920K)] [ParOldGen: 584K->585K(40960K)] 680K->585K(58880K), [Metaspace: 2668K->2668K(1056768K)], 0.0055624 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
100
null
-------------test04-------------
Heap
 PSYoungGen      total 17920K, used 614K [0x00000000ec500000, 0x00000000ed900000, 0x0000000100000000)
  eden space 15360K, 4% used [0x00000000ec500000,0x00000000ec599b20,0x00000000ed400000)
  from space 2560K, 0% used [0x00000000ed680000,0x00000000ed680000,0x00000000ed900000)
  to   space 2560K, 0% used [0x00000000ed400000,0x00000000ed400000,0x00000000ed680000)
 ParOldGen       total 40960K, used 585K [0x00000000c4e00000, 0x00000000c7600000, 0x00000000ec500000)
  object space 40960K, 1% used [0x00000000c4e00000,0x00000000c4e92440,0x00000000c7600000)
 Metaspace       used 2675K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

4 总结

 强引用  >  软引用  >  弱引用  >  虚引用

哈哈哈哈,总体来说就是按需使用。尽量循环使用已有对象(使用对象池),不要长期保存无用对象,缓存中的对象多多关注。

 

 


爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qqchaozai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值