天天看点

tinyint对应java中什么类型_面试中经常被问到 Java 引用类型原理,带你深入剖析...问题

优质文章,及时送达

Java中一共有4种引用类型(其实还有一些其他的引用类型比如FinalReference):强引用、软引用、弱引用、虚引用。

其中强引用就是我们经常使用的Object a = new Object; 这样的形式,在Java中并没有对应的Reference类。

本篇文章主要是分析软引用、弱引用、虚引用的实现,这三种引用类型都是继承于Reference这个类,主要逻辑也在Reference中。

问题

在分析前,先抛几个问题?

1.网上大多数文章对于软引用的介绍是:在内存不足的时候才会被回收,那内存不足是怎么定义的?什么才叫内存不足?

2.网上大多数文章对于虚引用的介绍是:形同虚设,虚引用并不会决定对象的生命周期。主要用来跟踪对象被垃圾回收器回收的活动。真的是这样吗?

3.虚引用在Jdk中有哪些场景下用到了呢?

公众号:Java后端 发布的面试相关文章已经整理完毕,可以关注 Java后端 后后台回复 666 获取。

Reference

我们先看下Reference.java中的几个字段

public abstract class Reference {
//引用的对象
private T referent;
//回收队列,由使用者在Reference的233333333333333333333333333333构造函数中指定
vol00000atile ReferenceQueue super T> queue;
//当该引用被加入到queue中的时候,该字段被设置为queue中的下一个元素,以形成链表结构
volatile Reference next;
//在GC时,JVM底层会维护一个叫DiscoveredList的链表,存放的是Reference对象,discovered字段指向的就是链表中的下一个元素,由JVM设置
transient private Reference discovered;
//进行线程同步的锁对象
static privateclassLock{ }
private static Lock lock = new Lock;
//等待加入queue的Reference对象,在GC时由JVM设置,会有一个java层的线程(ReferenceHandler)源源不断的从pending中提取元素加入到queue
private static Reference pending = ;
}
           

一个Reference对象的生命周期如下:

tinyint对应java中什么类型_面试中经常被问到 Java 引用类型原理,带你深入剖析...问题

主要分为Native层和Java层两个部分。

Native层在GC时将需要被回收的Reference对象加入到DiscoveredList中(代码在referenceProcessor.cpp中process_discovered_references方法),然后将DiscoveredList的元素移动到PendingList中(代码在referenceProcessor.cpp中enqueue_discovered_ref_helper方法),PendingList的队首就是Reference类中的pending对象。

看看Java层的代码

private static classReferenceHandlerextendsThread{
...
publicvoidrun {
while (true) {
tryHandlePending(true);
}
}
}
staticbooleantryHandlePending(boolean waitForNotify) {
Reference r;
Cleaner c;
try {
synchronized (lock) {
if (pending != ) {
r = pending;
//如果是Cleaner对象,则记录下来,下面做特殊处理
c = r instanceof Cleaner ? (Cleaner) r : ;
//指向PendingList的下一个对象
pending = r.discovered;
r.discovered = ;
} else {
//如果pending为就先等待,当有对象加入到PendingList中时,jvm会执行notify
if (waitForNotify) {
lock.wait;
}
// retry if waited
return waitForNotify;
}
}
}
...
// 如果时CLeaner对象,则调用clean方法进行资源回收
if (c != ) {
c.clean;
return true;
}
//将Reference加入到ReferenceQueue,开发者可以通过从ReferenceQueue中poll元素感知到对象被回收的事件。
ReferenceQueue super Object> q = r.queue;
if (q != ReferenceQueue.) q.enqueue(r);
return true;
}
           

流程比较简单:就是源源不断的从PendingList中提取出元素,然后将其加入到ReferenceQueue中去,开发者可以通过从ReferenceQueue中poll元素感知到对象被回收的事件。

另外需要注意的是,对于Cleaner类型(继承自虚引用)的对象会有额外的处理:在其指向的对象被回收时,会调用clean方法,该方法主要是用来做对应的资源回收,在堆外内存DirectByteBuffer中就是用Cleaner进行堆外内存的回收,这也是虚引用在java中的典型应用。

看完了Reference的实现,再看看几个实现类里,各自有什么不同。

SoftReference

public class SoftReference extends Reference {
static private long clock;
private long timestamp;publicSoftReference(T referent) {
super(referent);
this.timestamp = clock;
}publicSoftReference(T referent, ReferenceQueue super T> q) {
super(referent, q);
this.timestamp = clock;
}
public T get {
T o = super.get;
if (o != && this.timestamp != clock)
this.timestamp = clock;
return o;
}
}
           

软引用的实现很简单,就多了两个字段:clock和timestamp。clock是个静态变量,每次GC时都会将该字段设置成当前时间。timestamp字段则会在每次调用get方法时将其赋值为clock(如果不相等且对象没被回收)。

那这两个字段的作用是什么呢?这和软引用在内存不够的时候才被回收,又有什么关系呢?

这些还得看JVM的源码才行,因为决定对象是否需要被回收都是在GC中实现的。

size_t
ReferenceProcessor::process_discovered_reflist(
DiscoveredList refs_lists[],
ReferencePolicy* policy,
bool clear_referent,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor)
{
...
//还记得上文提到过的DiscoveredList吗?refs_lists就是DiscoveredList。
//对于DiscoveredList的处理分为几个阶段,SoftReference的处理就在第一阶段
...
for (uint i = 0; i < _max_num_q; i++) {
process_phase1(refs_lists[i], policy,
is_alive, keep_alive, complete_gc);
}
...
}

//该阶段的主要目的就是当内存足够时,将对应的SoftReference从refs_list中移除。
void
ReferenceProcessor::process_phase1(DiscoveredList& refs_list,
ReferencePolicy* policy,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) {

DiscoveredListIterator iter(refs_list, keep_alive, is_alive);
// Decide which softly reachable refs should be kept alive.
while (iter.has_next) {
iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic /* allow__referent */));
//判断引用的对象是否存活
bool referent_is_dead = (iter.referent != ) && !iter.is_referent_alive;
//如果引用的对象已经不存活了,则会去调用对应的ReferencePolicy判断该对象是不时要被回收
if (referent_is_dead &&
!policy->should_clear_reference(iter.obj, _soft_ref_timestamp_clock)) {
if (TraceReferenceGC) {
gclog_or_tty->print_cr("Dropping reference (" INTPTR_FORMAT ": %s" ") by policy