天天看點

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