导言
在JDK1.2之前Java并没有供给软引证、弱引证和虚引证这些高级的引证类型。而是供给了一种基本的引证类型,称为Reference
。并且当时Java中的目标只要两种状态:被引证和未被引证。当一个目标被引证时,它将一直存在于内存中,直到它不再被任何引证指向时,才会被废物收回器收回。而被引证也便是强引证。
而在JDK1.2之后对引证的概念进行了扩大,分为了强引证(StrongReference
)、软引证(SoftReference
)、弱引证(WeakReference
)和虚引证(PhantomReference
),这4种引证的强度顺次减弱。他们的联系如下如:
image.png
强引证
强引证是Java中最常见的引证类型。当你创立一个目标并将其赋值给一个变量时,这个变量会持有该目标的强引证。
Orderorder=newOrder();//只要order还指向Order目标,那么Order目标就不会被收回
order=null;//强引证都被设置为null时,不可达,则Order目标被收回
只要存在强引证指向目标,废物收回器将永久不会收回该目标,即便内存不足也不会收回。这或许导致内存溢出,由于即便内存不足,JVM也不会收回强引证目标。当强引证都被设置为null时,目标变成不可达状态,废物收回器会在适当的时候将其收回。
比方以下示例,咱们创立一个2M的数组,可是咱们设置JVM参数:-Xms2M -Xmx3M
,将JVM的初始内存设为2M,最大可用内存为3M。
publicstaticvoidmain(String[]args){
//界说一个2M的数组
byte[]objects=newbyte[1024*1024*2];
}
此刻咱们履行办法后,发现报错:
image.png
关于强引证,即便内存不够运用,直接报错OOM,强引证也不会被收回。
关于强引证,就比方日子中,当咱们具有家里的钥匙时,咱们能够随时进入你的家,即便咱们不需求进入,也能确保咱们能够进入。钥匙是咱们进入家的强引证。只要当咱们不再具有钥匙时,咱们才无法进入家,类似于当没有强引证指向一个目标时,该目标才能被废物收回。
软引证
在JDK1.2之后,用java.lang.ref.SoftReference
类来表明软引证。软引证允许目标在内存不足时被废物收回器收回。假如一个目标只要软引证指向它,当体系内存不足时,废物收回器会尝试收回这些目标来开释内存,假如收回了软引证目标之后仍然没有足够的内存,才会抛出内存溢出反常。软引证适用于需求缓存很多目标,但又期望在内存不足时开释部分目标以防止内存溢出的情况,用于完成缓存时,当内存紧张时,能够开释部分缓存目标以确保体系的稳定性。
以下示例咱们设置JVM参数为:-Xms3M -Xmx5M
,然后接连创立了10个巨细为1M的字节数组,并赋值给了软引证,然后循环遍历将这些目标打印出来。
privatestaticfinalList<Object>list=Lists.newArrayList();
publicstaticvoidmain(String[]args){
IntStream.range(0,10).forEach(i->{
byte[]buff=newbyte[1024*1024];
SoftReference<byte[]>sr=newSoftReference<>(buff);
list.add(sr);
});
System.gc();//主动告诉废物收回
list.forEach(l->{
Objectobj=((SoftReference<?>)l).get();
System.out.println("Object:"+obj);
});
}
然后咱们履行代码之后:
image.png
关于打印成果中,只要最终一个目标保留了下来,其他的obj全都被置空收回了。即说明晰在内存不足的情况下,软引证将会被自动收回。
关于弱引证,就像咱们医药箱里的备用药,当咱们需求药品时,咱们会先看看医药箱里是否有备用药。假如医药箱里有足够的药品(内存足够),咱们就能够运用备用药;但假如医药箱里的备用药不够了(内存不足),咱们或许会去药店购买。在内存不足时,废物收回器或许会收回软引证目标,类似于咱们在医药箱里的备用药被用完时去药店购买。
弱引证
JDK1.2之后,用java.lang.ref.WeakReference
来表明弱引证。弱引证与软引证类似,但强度更弱。即便内存足够,只要没有强引证指向一个目标,废物收回器就能够随时收回该目标。弱引证适用于需求临时引证目标的场景,如临时缓存或临时存储目标。也能够用于处理目标之间的循环引证问题,防止内存泄漏。
关于上述示例中,咱们将数组赋值给弱引证
privatestaticfinalList<Object>list=Lists.newArrayList();
publicstaticvoidmain(String[]args){
IntStream.range(0,10).forEach(i->{
byte[]buff=newbyte[1024*1024];
WeakReference<byte[]>sr=newWeakReference<>(buff);
list.add(sr);
});
System.gc();//主动告诉废物收回
list.forEach(l->{
Objectobj=((WeakReference<?>)l).get();
System.out.println("Object:"+obj);
});
}
履行成果发现所有的目标都是null,即都被收回了。
image.png
关于弱引证,就像咱们正在旅行,运用一张一次性地图。咱们只在需求导航时运用地图,一旦旅行完毕,咱们就不再需求地图了。这时咱们能够选择丢掉地图,类似于弱引证,在废物收回器运行时,不管内存是否足够,目标都或许被收回。
虚引证
在 JDK1.2之后,用java.lang.ref.PhantomReference
类来表明虚引证。虚引证是最弱的引证类型,它简直对目标没有任何影响,不能经过虚引证获取目标,也不能经过它来阻止目标被废物收回。从源码中能够看出它只要一个结构函数和一个 get() 办法,并且它的 get() 办法仅仅是回来一个null,也便是说将永久无法经过虚引证来获取目标,虚引证必须要和 ReferenceQueue 引证行列一起运用。
image.png
虚引证能够用于在目标被收回时进行后续操作,如目标资源开释或日志记载,常用于跟踪目标被废物收回的状态,履行一些整理作业。
而关于弱引证,就像咱们去商铺,商铺入口处的门闩并不直接影响你进入房子,但它会在有人进入或脱离时发出声音,提示你有人进店(欢迎光临)或许脱离(欢迎再来)。类似地,虚引证并不直接影响目标的生命周期,但它能够在目标被收回时发出告诉,让你有机会进行一些后续操作,比方资源开释或许记载日志。
引证行列
引证行列(ReferenceQueue
)是Java中的一个特别行列,用于合作软引证、弱引证和虚引证,完成更灵活的目标引证和收回办理。
引证行列的首要作用是跟踪目标的废物收回过程。当一个软引证、弱引证或虚引证指向的目标被废物收回器收回时,假如它们与一个引证行列关联,那么这些引证就会被自动加入到引证行列中。经过监视引证行列中的目标,咱们能够了解到目标的收回状态,然后履行一些额定的操作,比方资源开释或日志记载等。
总结
Java中的四种引证类型各具特点,可根据程序需求选择合适的引证类型。强引证确保目标不被意外收回,软引证和弱引证用于完成缓存或处理内存敏感问题,而虚引证则用于目标收回后的告诉和整理操作。合理运用引证类型能够更好地办理内存和防止内存泄漏问题。
本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包含Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等