翻译起来比较简单,引用(reference object)是一个间接层,我们的代码通过引用访问引用指向的对象(referent)。
目前累计服务客户数千家,积累了丰富的产品开发及服务经验。以网站设计水平和技术实力,树立企业形象,为客户提供网站设计、成都网站设计、网站策划、网页设计、网络营销、VI设计、网站改版、漏洞修补等服务。成都创新互联始终以务实、诚信为根本,不断创新和提高建站品质,通过对领先技术的掌握、对创意设计的研究、对客户形象的视觉传递、对应用系统的结合,为客户提供更好的一站式互联网解决方案,携手广大客户,共同发展进步。
relationships between application code, soft/weak reference, and referent
所有的引用类型,都是抽象类 java.lang.ref.Reference 的子类:
这个抽象类提供了 get 方法用来获取引用指向的对象(referent):
举个例子:
SoftReference> ref = new SoftReference
>(new LinkedList
());
// somewhere else in your code, you create a Foo that you want to add to the list
Listlist = ref.get();
if (list != null) {
list.add(foo);
} else {
// list is gone; do whatever is appropriate
}
我想大部分人都可以很轻松的说出引用的定义:如果栈中的变量存储的数值代表的是另外一块内存的起始地址,就称该变量代表了这块内存 or 这个对象的引用。
在 JDK 1.2 之前,没有问题,这个定义很正确。
不过现在来看有些过于狭隘了。
举个例子,我们希望引用能够描述这样一类对象:当内存空间还足够时,就保留在内存之中,如果垃圾收集后内存空间比较紧张,那就抛弃这些对象释放空间。
对于上述的定义来说,一个对象只有 “被引用” 和 “未被引用” 两种状态,对这种情况显然是无能无力的。
所以,JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为以下四种,这 4 种引用的强度依次逐渐减弱,所谓 “强度”,可以这样简单理解,引用的强度越强,那么这个被引用的对象就越不容易被垃圾回收器回收掉:
强引用随处可见,就是最传统的 “引用” 的定义,通过 new 进行的引用赋值,即类似User user = new User() 这种引用关系。
只要还有强引用指向一个对象,就能表明对象还 “活着”,垃圾收集器永远不会碰这种对象。
换句话说,当内存空间不足的时候,JVM 宁可抛出 OOM,使程序异常终止,也不会回收具有强引用的对象。
软引用就对应我们上面举的那个例子,可以让对象豁免一些垃圾收集,用来描述一些还有用、但非必须的对象。
如果内存空间足够,那么软引用就不会被回收掉,但是如果快要发生 OOM 了,那么 JVM 就会对这些软引用进行回收释放空间,如果对这些软引用回收完了之后还是没有足够的内存,才会抛出 OOM。
在 JDK 1.2 之后提供了 SoftReference 类来实现软引用。
弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些。
如果你创建了一个仅持有弱引用的对象,那么下一次垃圾收集发生的时候,无论当前内存是否足够,这个对象都会被回收掉。
换句话说,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。
在 JDK 1.2 之后提供了 WeakReference 类来实现弱引用。
虚引用也称为 幽灵引用、幻影引用、幻象引用,它是最弱的一种引用关系。
如果一个对象仅持有幻像引用,那么它就和没有任何引用一样,对其生存时间没有任何影响,我们也无法通过幻像引用来取得一个对象实例(看下图,它的 get 方法永远返回 null)。
虚引用的 get 方法
滑稽了,那幻像引用有啥用?
事实上,我们可以通过为一个对象设置幻像引用关联从而跟踪这个对象被垃圾回收的活动(详细见下文解释)。
在 JDK 1.2 之后提供了 PhantomReference 类来实现幻像引用。
在 JDK1.2 之前,一个对象的生命周期(object life cycle)可以简单的用下图表示:
object life-cycle, without reference objects
而在 JDK1.2 中,引入了 java.lang.ref 包,一个对象的生命周期中新增了三个状态(stage):
可以看到,除了强引用对应的强可达状态(strongly reachable)之外,额外添加了个三个状态,分别对应软引用、弱引用和虚引用(幻像引用):
除了幻像引用(因为 get 永远返回 null),如果对象还没有被销毁,都可以通过 get 方法获取原有对象。这意味着,利用软引用和弱引用,我们可以将访问到的对象,重新指向强引用,也就是人为的改变了对象的可达性状态!这也是为什么上面图里有些地方画了双向箭头。
引用队列 ReferenceQueue 是用来配合引用工作的,最常与幻像引用一起使用,因为幻像引用的构造函数必须指定引用队列,而其他引用类型没有引用队列一样可以运行。
当某个被引用的对象(referent)被回收的时候,JVM 会将指向它的引用(reference)加入到引用队列的队列末尾,这相当于是一种通知机制。这个操作其实是由 ReferenceHandler 守护线程来做的,这个守护线程是在 Reference 静态代码块中建立并且运行的线程,所以只要 Reference 这个父类被初始化,该线程就会创建和运行,它的运行方法中依赖了比较多的本地 (native) 方法:
由于 ReferenceHandler 是守护线程,除非 JVM 进程终结,否则它会一直在后台运行(注意它的 run() 方法里面使用了死循环)。
实际上就是调用了引用队列的 enqueue 方法来执行入队操作:
这样,我们可以通过 ReferenceQueue 中的元素(引用)来知道哪些对象(被引用的对象)被回收掉了,通过这种方式,我们就可以在对象被回收掉之后,做一些我们自己想做的事情。
这也就是为什么说幻像引用存在的唯一作用就是跟踪对象被垃圾回收的活动。
另外,ReferenceQueue 提供了三种方法来弹出队头元素:
断路器,Circuit Breaker
举个例子,下面这段 JDBC 代码,逻辑是查询数据库的多行数据。
往比较极端的情况想,如果查询到的数据有一百万行,但你的系统的可用内存资源已经不足以装得下这一百万行数据,此时程序肯定就抛错误了。
这个时候软引用的价值就体现出来了:如果在查询数据期间 JVM 已经耗尽了内存,那么被软引用指向的对象的内存就会被释放掉从而给新的数据挪出空间,同时在业务线程上我们可以抛出自定义异常以便我们进行程序的后续处理:
大名鼎鼎,这个本文就不多说了,后续会开文章详细解释。
数据库连接池 Connection Pool 应该具备的一个优点就是能够有效的避免连接资源泄露,同时能够对连接资源进行回收:
下面这个类可以不用怎么看,不过有一点值得注意,用户使用该连接池时业务线程拿到的连接对象正是这个PooledConnection 对象,而不是真正的 Connection 对象。
重点看下下面这个类的实现:
如果引用队列中能够拿到引用,说明连接对象被 GC 掉了,此时我们就应该对连接池执行相应的清理逻辑(重点注意下面的 releaseConnection 方法):
看起来挺复杂,其实本质上就是围绕着虚引用的特性:你不能通过它访问对象,但是它结合引用队列提供了一种对象被回收以后做某些事情的机制。
标题名称:偏僻又热门,引用与引用队列
文章URL:http://www.shufengxianlan.com/qtweb/news30/306180.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联