java中的Reference
这两天又重新学习了一下Reference,根据网上的资源做了汇总。
Java中的引用主要有4种:
强引用 StrongReference: Object obj = new Object(); obj就为一个强引用,obj=null后, 该对象可能会被JVM回收
软引用 SoftReference: 在内存不够用的时候,才会回收软引用的对象。
Object obj = new Object();
SoftReference<Object> softref = new SoftReference<Object>(obj);
obj = null;
弱引用 WeakReference: new出来的对象没有强引用连接时,下一次GC时,就会回收该对象。
Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<Object>(obj);
obj = null;
虚引用 PhantomReference: 与要与ReferenceQueue配合使用,它的get()方法永远返回null
JDK中的 java.lang.ref包:
java.lang.ref包下主要都是reference相关的类,主要包括:
FinalReference: 代表强引用,使没法直接使用。
Finalizer:FinalReference的子类,主要处理finalize相关的工作
PhantomReference: 虚引用
Reference: 引用基类,abstract的
ReferenceQueue: 引用轨迹队列
SoftReference:软引用
WeakedReference: 弱引用
ReferenceQueue
引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中。
static ReferenceQueue NULL = new Null(); //初始化为null
static ReferenceQueue ENQUEUED = new Null(); static private class Lock { };
private Lock lock = new Lock(); //初始化为null
private volatile Reference<? extends T> head = null; //Queue的header设定为null
private long queueLength = 0;
queue主要有几种操作: enqueue, poll(非阻塞), remove(阻塞)
enqueue的操作是添加一个对象到队列中。
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
//本方法只能在Reference类中调用
synchronized (r) {
//防止将同一个Reference两次入队列
if (r.queue == ENQUEUED) return false;
synchronized (lock) {
//该对象入队列后,将该对象的queue对象置为 ENQUEUED
r.queue = ENQUEUED;
r.next = (head == null) ? r : head;
head = r;
queueLength++;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(1);
}
lock.notifyAll();
return true;
}
}
}
入队列的实际操作就是将Reference对象r放到header的第一位
poll的方法为:public Reference<? extends T> poll(),最终会调用 private Reference<? extends T> reallyPoll();具体操作与添加相反,若队列没有什值,直接返回null。
remove的方法:public Reference<? extends T> remove(),最终会调用public Reference<? extends T> remove(long timeout),传递超时时间.
//阻塞,直到返回结果
public Reference<? extends T> remove(long timeout)
throws IllegalArgumentException, InterruptedException
{
if (timeout < 0) {
throw new IllegalArgumentException("Negative timeout value");
}
synchronized (lock) {
Reference<? extends T> r = reallyPoll();
if (r != null) return r;
//死循环
for (;;) {
lock.wait(timeout);
r = reallyPoll();
if (r != null) return r;
//若传递的等待时间不为0,说明等该timeout后也没有reference入队列,返回null,
//若timeout为0,则循环,直接队列有数据
if (timeout != 0) return null;
}
}
}
SoftReference
SoftReference继承了抽象类Reference,自己内容有两个属性:
/**
* Timestamp clock, updated by the garbage collector
*/
//GC 会更新这一个静态变量的值
static private long clock; /**
* Timestamp updated by each invocation of the get method. The VM may use
* this field when selecting soft references to be cleared, but it is not
* required to do so.
*/
//每次调用get方法时会更新timestampe
// this.timestamp = clock;
private long timestamp;
主要的逻辑还是在Reference类中:
Reference提供了两个构造方法:
Reference(T referent) {
this(referent, null);
} Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
同时有一些私有的属性:
private T referent; /* Treated specially by GC */ ReferenceQueue<? super T> queue; Reference next;
transient private Reference<T> discovered; /* used by VM */ static private class Lock { };
private static Lock lock = new Lock();
最有意思的是有段static的代码块
/* List of References waiting to be enqueued. The collector adds
* References to this list, while the Reference-handler thread removes
* them. This list is protected by the above lock object.
*/
//static类型,全局只有一个,GC一个Reference时,会把该Reference放到pending中,由于Reference有一个next属性,该处可能会是一些被GC过的引用的队列
private static Reference pending = null; /* High-priority thread to enqueue pending References
*/
private static class ReferenceHandler extends Thread { ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
} public void run() {
for (;;) { Reference r;
synchronized (lock) {
if (pending != null) {
//若pending不为null,将每个reference取出,
r = pending;
Reference rn = r.next;
pending = (rn == r) ? null : rn;
r.next = r;
} else {
//若pending为null,等侍
try {
lock.wait();
} catch (InterruptedException x) { }
continue;
}
} // Fast path for cleaners
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
} ReferenceQueue q = r.queue;
//找到该Reference对象中的Queue,将自己添加到ReferenceQueue中
//这样就实现了软引用对象被回收后,在ReferenceQueue中就可以获取到。
if (q != ReferenceQueue.NULL) q.enqueue(r);
}
}
}
//启动一个线程(Reference Handler)处理引用
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
}
其它的一些操作如:get, clear, isEnqueued, enqueue,都是简单的操作。
WeakReference
WeakReference也继承了Reference对象。
WeakReference与SoftReference
这两上类都继承了Reference对象,基本的操作都一样的。唯一的区别就是SoftReference内部的属性(private long timestamp; 在每次get的时候会更新该值),VM有可能要GC的时候使用该字段来判断。这就和两类引用的区别相关连了,WeakReference每次GC时就会直接回收该引用的对象,而SoftReference只有在内存不够用的时候才会回收对象,而回收哪一个对象,可能就需要这个字段来区分。
FinalReference
class FinalReference<T> extends Reference<T> { public FinalReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
} }
这个类的权限是package,我们没法new出一个对象来使用。并且也只是继承了Reference类,没有别的特殊的操作。但同一个包有一类Finalizer,Finalizer继承了FinalReference,它也是一个package权限的类,同时是final的,不能被继承了。
//全局一个ReferenceQueue
private static ReferenceQueue queue = new ReferenceQueue();
//全局只有一个unfinalized,也可以组成对象链
private static Finalizer unfinalized = null;
private static final Object lock = new Object(); private Finalizer
next = null,
prev = null; // 将自己添加到unfinalized队列中
private void add() {
synchronized (lock) {
if (unfinalized != null) {
this.next = unfinalized;
unfinalized.prev = this;
}
unfinalized = this;
}
}
//私有的构造方法
private Finalizer(Object finalizee) {
//任何Finalizer对象的GC后都会到queue中
super(finalizee, queue);
add();
}
/* Invoked by VM */
static void register(Object finalizee) {
new Finalizer(finalizee);
}
register执行的时候会new出对象,只有f类才会被JVM调用register。
f类:实现了finalize方法并且非空的类。类的加载过程就已经标记为是否f类。
Finalizer类最后有一些static的代码块:
private static class FinalizerThread extends Thread {
private volatile boolean running;
FinalizerThread(ThreadGroup g) {
super(g, "Finalizer");
}
public void run() {
if (running)
return; // Finalizer thread starts before System.initializeSystemClass
// is called. Wait until JavaLangAccess is available
while (!VM.isBooted()) {
// delay until VM completes initialization
try {
VM.awaitBooted();
} catch (InterruptedException x) {
// ignore and continue
}
}
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
try {
//从ReferenceQueue中取出对象,执行对象的runFinalizer方法
Finalizer f = (Finalizer)queue.remove();
f.runFinalizer(jla);
} catch (InterruptedException x) {
// ignore and continue
}
}
}
} static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
//线程优先级低
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
finalizer.setDaemon(true);
finalizer.start();
}
runFinalize方法会通过JVM调用object的finalize方法
private boolean hasBeenFinalized() {
return (next == this);
}
private void runFinalizer(JavaLangAccess jla) {
synchronized (this) {
//若next==this,则表明this对象已经从unfinalized对象链中移除,已经执行过一次runFinalizer了
if (hasBeenFinalized()) return;
//将该对象从unfinalized对象链中移除
remove();
}
try {
Object finalizee = this.get();
if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
//通过JDK调用对象的finalize方法
jla.invokeFinalize(finalizee); /* Clear stack slot containing this variable, to decrease
the chances of false retention with a conservative GC */
finalizee = null;
}
} catch (Throwable x) { }
super.clear();
}
1 当GC发生时,GC算法会判断f累对象是不是只被Finalizer类引用
2若这个累仅仅被Finalizer对象引用,说明这个对象在不就的将来会被回收,现在可以执行他的inalize方法了。
3 将这个队形放到Finalizer类的ReferenceQueue中,但这个f类对象其实并没有被回收,因为Finalizer这个类还对他们保持引用。
4 GC完成之前,JVM会调用ReferenceQueue中lock对象的notify方法,
5 Finalizer的守护线程可能会被唤醒,从Queue中取出对象(remove),执行该Finalizer对象的runFinalizer方法(1 将自己从unfinalized对象链中去除,2 执行引用对象的finalize方法)
6 下次GC时回收这个对象。
- f对象因为
Finalizer
的引用而变成了一个临时的强引用,即使没有其他的强引用,还是无法立即被回收; - f对象至少经历两次GC才能被回收,因为只有在
FinalizerThread
执行完了f对象的finalize
方法的情况下才有可能被下次GC回收,而有可能期间已经经历过多次GC了,但是一直还没执行f对象的finalize
方法; - CPU资源比较稀缺的情况下
FinalizerThread
线程有可能因为优先级比较低而延迟执行f对象的finalize
方法; - 因为f对象的
finalize
方法迟迟没有执行,有可能会导致大部分f对象进入到old分代,此时容易引发old分代的GC,甚至Full GC,GC暂停时间明显变长; - f对象的
finalize
方法被调用后,这个对象其实还并没有被回收,虽然可能在不久的将来会被回收。 - You can see preference processing times (and number of references) in GC logs with
-XX:+PrintReferenceGC
参考:
java中的Reference的更多相关文章
- Java中的Reference类使用
Java 2 平台引入了 java.lang.ref 包,这个包下面包含了几个Reference相关的类,Reference相关类将Java中的引用也映射成一个对象,这些类还提供了与垃圾收集器(gar ...
- Java中primitive type的线程安全性
Java中primite type,如char,integer,bool之类的,它们的读写操作都是atomic的,但是有几个例外: long和double类型不是atomic的,因为long和doub ...
- 理解Java中的弱引用(Weak Reference)
本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...
- Java中对不变的 data和object reference 使用 final
Java中对不变的 data和object reference 使用 final 许多语言都提供常量数据的概念,用来表示那些既不会改变也不能改变的数据,java关键词final用来表示常量数据.例如: ...
- java中的方法引用(method reference)官方文档总结
2017/7/5 转载写明出处:http://www.cnblogs.com/daren-lin/p/java-method-reference.html 今天要说的是java中的一项新特性,方法引用 ...
- java 中 Cannot make a static reference to the non-static 解决方法
今天敲代码的时候遇到了这个问题,大体这个问题可以简化成这样: public class Test1 { public String get() { return "123"; } ...
- (转)浅谈Java中的对象和对象引用
原文地址: http://www.cnblogs.com/dolphin0520/p/3592498.html 在Java中,有一组名词经常一起出现,它们就是"对象和对象引用",很 ...
- java中关键字volatile的作用
用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...
- java中Map,List与Set的区别(转)
Set,List,Map的区别 java集合的主要分为三种类型: Set(集) List(列表) Map(映射) 要深入理解集合首先要了解下我们熟悉的数组: 数组是大小固定的,并且同一个数组只能存放类 ...
随机推荐
- Vue.js项目部署在Tomcat服务器上
1.在本地的Vue框架中 执行npm run build 将我们的项目打包到dist 文件夹中 2.在服务器上的Tomcat的 webapps文件夹下,新建一个文件夹如:frontvue 3.启动t ...
- Mysql CASE WHEN 用法
select sum(1) as col_0_0_, sum(case vciinfo.useable when -1 then 1 else 0 end) as col_1_0_, sum(case ...
- tornado之WebSocket
WebSocket WebSocket是HTML5规范中新提出的客户端-服务器通讯协议,协议本身使用新的ws://URL格式. WebSocket 是独立的.创建在 TCP 上的协议,和 HTTP 的 ...
- 线程实现Runnable接口比继承Thread的优势
1.适合多个相同程序代码的线程去处理同一资源,把虚拟CPU(线程)同程序的代码.数据有效分离,较好地体现了面向对象的设计思想.2.可以避免由于java单继承特性带来的局限.3.增强了程序的健壮性,代码 ...
- 转载 URL短地址压缩算法
文章转载http://www.nowamagic.net/webdesign/webdesign_ShortUrlInTwitter.php /// <summary> /// 生成sal ...
- WPF圆角透明无边框窗体
<Window x:Class="ImportData.MainWindow" xmlns="http://schemas.microsoft.com/winfx/ ...
- codeforces246E Blood Cousins Return
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- 基于usb4java实现的java下的usb通信
项目地址:点击打开 使用java开发的好处就是跨平台,基本上java的开发的程序在linux.mac.MS上都可以运行,对应这java的那句经典名言:一次编写,到处运行.这个项目里面有两种包选择,一个 ...
- 将springboot项目发布到独立的tomcat中运行
在开发阶段我们推荐使用内嵌的tomcat进行开发,因为这样会方便很多,但是到生成环境,我希望在独立的tomcat容器中运行,因为我们需要对tomcat做额外的优化,这时我们需要将工程打包成war包发进 ...
- Android真机调试——远程主机强迫关闭了一个现有的连接。
以前用真机调试程序的时候,Android Studio 出现如下的错误 [2016-11-12 10:37:36 - DeviceMonitor] Adb connection Error:远程主机强 ...