Java中的Reference类使用
Java 2 平台引入了 java.lang.ref 包,这个包下面包含了几个Reference相关的类,Reference相关类将Java中的引用也映射成一个对象,这些类还提供了与垃圾收集器(garbage collector)之间有限的交互。
Reference引用类的几种类型
在jvm中,一个对象如果不再被使用就会被当做垃圾给回收掉,判断一个对象是否是垃圾,通常有两种方法:引用计数法和可达性分析法。不管是哪一种方法判断一个对象是否是垃圾的条件总是一个对象的引用是都没有了。
JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用、软引用、弱引用、虚引用4 种。下面就介绍下这些引用类型的区别。
强引用
如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。下面的代码中str就是一个强引用。
public void test1(){
String str = new String("程序员自由之路");
}
软引用(SoftReference)
内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。
上面只是很简单的说了下:当系统没有足够的内存时会回收软引用对象。但是具体什么才是内存不够?具体的回收具体是什么?如果想要了解具体的情况,大家可以参考这篇文章。我简单总结了下,软引用对象具体的回收策略如下:
如果已经没有引用指向软引用对象,那么这个对象会被JVM回收;
如果还有软引用指向这个软引用对象,就判断在某段之间之内(_max_interval),有没有调用过SoftReference的get方法,如果在_max_interval时间内没调用过get方法,那么即使还有软引用指向这个对象,JVM也会回收这个对象,如果在_max_interval时间内调用过get方法,那么就不会回收这个对象。
_max_interval具体的时间是根据JVM的可用内存动态计算出来的,如果JVM的可用内存比较大,那么_max_interval的值也比较大,如果JVM的可用内存比较小,那么max_interval也会比较小。
我自己写了一段代码来展示软引用对象回收的过程。为了让堆内存迅速耗尽,我将最大内存设置为-Xmx5m。
public static void main(String[] args) throws InterruptedException {
SoftReference<String> reference = new SoftReference<>(new String("自由之路..."));
List<String> list = new ArrayList<>();
while (true) {
for (int i = 0; i < 10000; i++) {
// 这边的对象都是强引用,不会被回收
list.add(new String("自由之路"));
}
// 暂停一段时间,为了让_max_interval时间段检测生效
// 没有这段暂停的话,JVM不会回收软引用对象,因为一直有线程在快速地调用软引用的get方法
TimeUnit.MILLISECONDS.sleep(10);
String s = reference.get();
if (s == null) {
logger.info("OMG, reference is gone...");
}else {
logger.info(s);
}
}
}
代码的执行效果,如下:
13:36:52.322 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.372 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.385 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.397 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.412 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.423 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.435 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.488 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.499 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:36:52.555 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
// 从下面开始,软引用对象已经被虚拟机回收了。
13:36:52.666 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
13:36:54.750 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
13:36:58.686 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
// 系统已经不能再分配出内存空间,直接报OutOfMemoryError
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.csx.demo.spring.boot.dao.UserMapperTest.main(UserMapperTest.java:54)
弱引用(WeakReference)
如果一个对象具有弱引用,在垃圾回收时候,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。
关于弱引用,我也写了个Bug代码,展示弱引用对象的回收过程。
public static void main(String[] args) throws InterruptedException {
WeakReference<String> reference = new WeakReference<>(new String("自由之路..."));
List<String> list = new ArrayList<>();
while (true) {
list.add(new String("自由之路"));
String s = reference.get();
if (s == null) {
logger.info("OMG, reference is gone...");
} else {
logger.info(s);
}
}
}
代码的执行结果如下:
13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
// 这边GC已经将弱引用对象回收
13:50:54.051 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
13:50:54.051 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
13:50:54.051 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
关于WeakReference,Java中一个比较典型的应用就是:WeakHashMap。关于这个类的使用情况大家可以参考这篇文章。
虚引用(PhantomReference)
虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个。一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例。
使用虚引用的目的就是为了得知对象被GC的时机,所以可以利用虚引用来进行销毁前的一些操作,比如说资源释放等。这个虚引用对于对象而言完全是无感知的,有没有完全一样,但是对于虚引用的使用者而言,就像是待观察的对象的把脉线,可以通过它来观察对象是否已经被回收,从而进行相应的处理。
在<<深入理解Java虚拟机>>3.2.3中有这么一句话
为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
public class Test {
public static boolean isRun = true;
@SuppressWarnings("static-access")
public static void main(String[] args) throws Exception {
String abc = new String("abc");
System.out.println(abc.getClass() + "@" + abc.hashCode());
final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
new Thread() {
public void run() {
while (isRun) {
Object obj = referenceQueue.poll();
if (obj != null) {
try {
Field rereferent = Reference.class
.getDeclaredField("referent");
rereferent.setAccessible(true);
Object result = rereferent.get(obj);
System.out.println("gc will collect:"
+ result.getClass() + "@"
+ result.hashCode() + "\t"
+ (String) result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}.start();
PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
referenceQueue);
abc = null;
Thread.currentThread().sleep(3000);
System.gc();
Thread.currentThread().sleep(3000);
isRun = false;
}
}
一个线程一直再检测回收队列中有没有被回收的引用。如果有被回收的引用,进行一些操作。
引用队列(ReferenceQueue)
作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法:
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(object, queue);
那么当这个SoftReference所软引用的对象被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收,于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。
SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
// 清除ref
}
参考
- https://www.cnblogs.com/renkangke/archive/2013/05/05/3061801.html
- https://blog.csdn.net/u014294681/article/details/86511451
Java中的Reference类使用的更多相关文章
- 带有静态方法的类(java中的math类)
带有静态方法的类通常(虽然不一定是这样)不打算被初始化. 可以用私有构造函数来限制非抽象类被初始化. 例如,java中的math类.它让构造函数标记为私有,所以你无法创建Math的实例.但Math类却 ...
- java中的File类
File类 java中的File类其实和文件并没有多大关系,它更像一个对文件路径描述的类.它即可以代表某个路径下的特定文件,也可以用来表示该路径的下的所有文件,所以我们不要被它的表象所迷惑.对文件的真 ...
- Java基础(43):Java中的Object类与其方法(转)
Object类 java.lang.Object java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. O ...
- java中基于TaskEngine类封装实现定时任务
主要包括如下几个类: 文章标题:java中基于TaskEngine类封装实现定时任务 文章地址: http://blog.csdn.net/5iasp/article/details/10950529 ...
- Java中的Unsafe类111
1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...
- Java中遍历实体类(处理MongoDB)
在实际过程中,经常要将实体类进行封装,尤其是处理数据库的过程中:因此,对于遍历实体类能够与数据库中的一行数据对应起来. 我是使用的环境是Spring boot,访问的数据库时MongoDB 实体类遍历 ...
- java中遍历实体类,获取属性名和属性值
方式一(实体类): //java中遍历实体类,获取属性名和属性值 public static void testReflect(Object model) throws Exception{ for ...
- Java中的BigDecimal类精度问题
bigdecimal 能保证精度的原理是:BigDecimal的解决方案就是,不使用二进制,而是使用十进制(BigInteger)+小数点位置(scale)来表示小数,就是把所有的小数变成整数,记录小 ...
- java 中常用的类
java 中常用的类 Math Math 类,包含用于执行基本数学运算的方法 常用API 取整 l static double abs(double a) 获取double 的绝对值 l sta ...
随机推荐
- linux 内核并发同步 2
信号量semaphore 信号量是一种允许进程进入睡眠的同步机制,信号量是一个计数器,支持两种原语即P 和V操作,也就是down 和up 操作, /* Please don't access any ...
- 多项目部署在同一个GitHub Pages
由于GitHub 的约定,一个账户只能拥有一个GitHub Pages,那么,如果你有多个想部署的静态网站(博客和文档等),它们是互相隔离的,如何用同一个GitHub账户进行部署呢? 从之前如何搭建G ...
- one-wallhaven 一个壁纸程序
one-wallhaven 一款基于 Electron 壁纸客户端 . gitee:https://gitee.com/ml13/wallhaven-electron github:https://g ...
- Python基础数据类型与for循环
数据类型:int,bool,str,list, tuple元组,dict字典. 1.数字:12,3,4 在使用print打印数字时,在终端界面中无法判断出打印的是什么类型,当我们需要知道一个值是什么类 ...
- webug第十四关:存储型XSS
第十四关:存储型XSS 打开发现是评论区 留言加入xss语句
- 面试BAT必问的JVM,今天我们来说一说它类加载器的底层原理
类加载器的关系 类加载器的分类 JVM支持两种类加载器,一种为引导类加载器(Bootstrap ClassLoader),另外一种是自定义类加载器(User Defined ClassLoader) ...
- level2行情是什么意思?
level2行情是由上海证券交易所推出的实时行情信息收费服务产品,主要提供在上海证券交易所上市交易的证券产品的实时交易数据,包括十档行情,买卖队列,逐笔成交,委托总量和加权价格等数据. 投资者根据其功 ...
- 关于oracle11g 和sqldeverloper的安装配置
0友情提示:以下下载地址都是我的百度云分享链接安全无毒请放心下载! 电脑配置 win10 jdk版本1.7 下载oracle11G 版本 下载地址:安装很简单 sqldverloper(oracleD ...
- C语言讲义——结构体struct
结构体是一种变量类型,可以包含多个变量(变量类型不必相同). 结构体的关键字是struct也是一种值类型. 例:设计一个表示"书本"的结构体: structBook { chari ...
- 如何让文科生5分钟写上Python
序言 这篇文章是转型后发表的第一篇文章,有必要先聊一下我的写文计划. 串行文章和并行文章 我会按照发文顺序分为串行文章和并行文章.Python 语言本身的内容,我会按照入门.进阶.原理的顺序依次展开. ...