一.是什么
在jdk1.2之前,java中引用的定义:如果引用类型存储的数值代表的是一块内存的起始地址,就称这块内存代表一个引用。在jdk1.2之后,java引入了4种对象引用类型,级别由高到低分别是:强引用、软引用、弱引用和虚引用。
二.为什么
为什么需要不同的引用
对于需要长期运行的应用程序来说,如果无用的对象所占的内存空间无法及时释放的话,那么在一个局部的时间段里就会形成事实上的内存泄露,如果要及时地释放内存,在java中最稳妥的方式就是在使用完对象过后 立即执行“object = null”,当然这这是理想状态。
通过这4种引用类型来解决内存泄露的问题,不同的引用方式会有不同的作用
- 强引用(StrongReference):直接引用对象,内存不足时也不会回收
- 软引用(SoftReference):内存不足时,回收引用相关联的对象
- 弱引用 (WeakReference):无论内存是否充足,都回收引用相关联的对象
- 虚引用(PhantomReference):任何时候都可以被垃圾回收器回收
强引用
强引用是日常编程中最常用的一种引用类型,它的特点是只要引用存在,就永远不会调用垃圾回收方法对其进行释放内存,java虚拟机宁可出现OutOfMemoryError错误停止运行,也会保存内存空间。只用当这个对象的引用被释放掉,对象才会被释放。
正是因为强引用具备这样的特点,所以我们的开发原则是尽量少实例化对象。
强引用是造成java内存泄露的主要原因之一。
软引用
软引用是指非必须引用,在内存足够时是不会对其进行回收,而在内存不足的时候垃圾回收器会将其回收并释放内存。java中软引用对应着SoftReference类,如果想要一个对象被软引用,只需要将其作为参数传入SoftReference类的构造方法中就行了。
软引用主要用于用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真实来源查询这些数据。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,java虚拟机就会把这个软引用加入到与之关联的队列引用中。
弱引用
弱引用相对于软引用,被弱引用的对象拥有更短的内存时间(生命周期)。垃圾回收器一旦发现了被弱引用的对象,就会回收它的内存,不管当前内存空间是不是足够。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定很快发现那些弱引用的对象。对应java中的WeakReference类,使用方法与软引用基本相同,弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收器回收,java虚拟机就会把这个软引用加入到与之关联的队列引用中。
弱引用主要应用于监控对象是否被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记
虚引用
顾名思义:形同虚设,虚引用并不会决定对象的生命周期,如果一个对象仅有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。程序也不能通过虚引用访问被引用的对象。
虚引用主要用来跟踪垃圾回收器回收的活动,虚引用与弱应用,软引用的区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象内存之前,把这个虚引用加入到与之关联的队列中。
代码
强引用
/**
* 强引用
* 只要对象存在一个强引用,对象不会被回收,即使gc运行
*/
public static void main(String[] args) {
Object object = new Object();//强引用
Object ref = object;//引用传递
System.out.println(object);//java.lang.Object@7cd84586
boolean equals = object.equals(new Object()); //可直接通过obj取得对应的对象
System.out.println(equals);//false
object = null;//断开引用与对象的链接
System.gc();
System.out.println(object);//null
System.out.println(ref);//java.lang.Object@7cd84586
}
弱引用
/**
* 弱引用
* gc 一运行,即刻被回收,即使剩余内存很大
*/
public static void main(String[] args) {
Object object = new Object();
WeakReference<Object> reference = new WeakReference<Object>(object);
object = null;
System.out.println(reference.get());//结果:java.lang.Object@610455d6
System.gc();
System.out.println(reference.get());//结果:null
}
软引用
/**
* 软引用
* gc 运行,并且即将OOM时,才会回收
*/
public static void main(String[] args) {
Object object = new Object();
SoftReference<Object> reference = new SoftReference<Object>(object);
object = null;
System.out.println(reference.get());//java.lang.Object@7cd84586
System.gc();//对比弱引用
System.out.println(reference.get());//java.lang.Object@7cd84586
//模拟OOM
final String str = "1111111111111111111111111111111111111111";//用于占用内存,创建一个final且字符较多的字符串
String string = "";
try {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
string = string + str;
string.intern();//该方法返回一个规范化表示的string对象。一个字符串常量池,初始化为空,且被String类私有地维护。
// 当intern()被调用时,如果常量池中存在和该String对象equal的对象(由equals方法决定),则返回常量池中已有的String对象,若不存在,则将该对象加入常量池,并返回该对象的引用。
}
} catch (Throwable throwable) {
System.out.println("--error--");
}
System.out.println(reference.get());//结果:null 切勿多次运行结果,否则电脑内存会约占越多,变卡
}
虚引用
/**
* 虚引用
*/
public static void main(String[] args) {
ReferenceQueue<String> queue = new ReferenceQueue<>();
String string = new String("hello");
PhantomReference<String> reference = new PhantomReference<String>(string,queue);
System.out.println(reference.get());//null
}
使用场景
一些工具类的单例,需要传入Context
静态内部类中使用弱引用来引用外部类的成员变量