這次把Java的四種引用一次性說清楚
前幾天在CodeReview的時候,看到了一個用WeakHashMap的程式碼,進而聊到了WeakReference,再聊到Java四種引用型別。
回想了一下,上次學習Java的強軟弱虛四種引用型別,還是在準備面試的時候。平時用得不多,一下子竟然想不清楚它們的區別,只記得它們的強度依次遞減。
下來又看了一下這方面的文章,今天好好把它們理清楚。
四種引用的區別
其實四種引用的區別在於GC的時候,對它們的處理不同。用一句話來概括,就是:如果一個物件GC Root可達,強引用不會被回收,軟引用在記憶體不足時會被回收,弱引用在這個物件第一次GC會被回收。
如果GC Root不可達,那不論什麼引用,都會被回收
虛引用比較特殊,等於沒有引用,不會影響物件的生命週期,但可以在物件被收集器回收時收到一個系統通知。
下面結合案例分別來講一下四種引用在面對GC時的表現以及它們的常見用途。先設定一下JVM的引數:
-Xms20M
-Xmx20M
-Xmn10M
-verbose
:gc
-XX
:+PrintGCDetails
強引用
這就是我們平時最常使用的引用。只要GC的時候這個物件GC Root可達,它就不會被回收。如果JVM記憶體不夠了,直接丟擲OOM。比如下面這段程式碼就會丟擲OutOfMemoryError:
public
static
void
main
(String[] args) {
List<Object>
list =
new LinkedList<>();
for (
int i =
0; i <
21; i++) {
list.add(
new byte[
1024 *
1024]);
}
}
軟引用
軟引用,當GC的時候,如果GC Root可達,如果記憶體足夠,就不會被回收;如果記憶體不夠用,會被回收。將上面的例子改成軟引用,就不會被OOM:
public
static
void
main(
String[] args) {
List<Object> list =
new LinkedList<>();
for (
int i =
0; i <
21; i++) {
SoftReference<
byte[]> softReference =
new SoftReference<>(
new
byte[
1024 *
1024]);
list.
add(softReference);
}
}
我們把程式改造一下,列印出GC後的前後的差別:
public
static
void
main(
String[] args) {
List<SoftReference<
byte[]>> list =
new LinkedList<>();
for (
int i =
0; i <
21; i++) {
SoftReference<
byte[]> softReference =
new SoftReference<>(
new
byte[
1024 *
1024]);
list.
add(softReference);
System.
out.println(
"gc前:" + softReference.
get());
}
System.gc();
for (SoftReference<
byte[]> softReference : list) {
System.
out.println(
"gc後:" + softReference.
get());
}
}
會發現,列印出的日誌,GC前都是有值的,而GC後,會有一些是null,代表它們已經被回收。
而我們設定的堆最大為20M,如果把迴圈次數改成15,就會發現列印出的日誌,GC後沒有為null的。但透過-verbose:gc -XX:+PrintGCDetails引數能發現,JVM還是進行了幾次GC的,只是由於記憶體還夠用,所以沒有回收。
public
static
void
main(
String[] args) {
List<SoftReference<
byte[]>> list =
new LinkedList<>();
for (
int i =
0; i <
15; i++) {
SoftReference<
byte[]> softReference =
new SoftReference<>(
new
byte[
1024 *
1024]);
list.
add(softReference);
System.
out.println(
"gc前:" + softReference.
get());
}
System.gc();
for (SoftReference<
byte[]> softReference : list) {
System.
out.println(
"gc後:" + softReference.
get());
}
}
所以軟引用的常見用途就呼之欲出了:快取。尤其是那種希望這個快取能夠持續時間長一點的。
弱引用
軟引用,只要這個物件發生GC,就會被回收。
把上面的程式碼改成軟引用,會發現列印出的日誌,GC後全部為null。
public
static
void
main(
String[] args) {
List<WeakReference<
byte[]>> list =
new LinkedList<>();
for (
int i =
0; i <
15; i++) {
WeakReference<
byte[]> weakReference =
new WeakReference<>(
new
byte[
1024 *
1024]);
list.
add(weakReference);
System.
out.println(
"gc前:" + weakReference.
get());
}
System.gc();
for (WeakReference<
byte[]> weakReference : list) {
System.
out.println(
"gc後:" + weakReference.
get());
}
}
所以弱引用也適合用來做快取,不過由於它是隻要發生GC就會被回收,所以存活的時間比軟引用短得多,通常用於做一些非常臨時的快取。
我們知道,WeakHashMap內部是透過弱引用來管理entry的。它的鍵是“弱鍵”,所以在GC時,它對應的鍵值對也會從Map中刪除。
Tomcat中有一個ConcurrentCache,用到了WeakHashMap,結合ConcurrentHashMap,實現了一個執行緒安全的快取,感興趣的同學可以研究一下原始碼,程式碼非常精簡,加上所有註釋,只有短短59行。
ThreadLocal中的靜態內部類ThreadLocalMap裡面的entry是一個WeakReference的繼承類。
使用弱引用,使得ThreadLocalMap知道ThreadLocal物件是否已經失效,一旦該物件失效,也就是成為垃圾,那麼它所操控的Map裡的資料也就沒有用處了,因為外界再也無法訪問,進而決定擦除Map中相關的值物件,Entry物件的引用,來保證Map總是保持儘可能的小。
虛引用
虛引用的設計和上面三種引用有些不同,它並不影響GC,而是為了在物件被GC時,能夠收到一個系統通知。
那它是怎麼被通知的呢?虛引用必須要配合ReferenceQueue,當GC準備回收一個物件,如果發現它還有虛引用,就會在回收之前,把這個虛引用加入到與之關聯的ReferenceQueue中。
那NIO是如何利用虛引用來管理記憶體的呢?
DirectBuffer直接從Java堆之外申請一塊記憶體, 這塊記憶體是不直接受JVM GC管理的, 也就是說在GC演算法中並不會直接操作這塊記憶體. 這塊記憶體的GC是由於DirectBuffer在Java堆中的物件被GC後, 透過一個通知機制, 而將其清理掉的.
DirectBuffer內部有一個Cleaner。這個Cleaner是PhantomReference的子類。當DirectBuffer物件被回收之後, 就會通知到PhantomReference。然後由ReferenceHandler呼叫tryHandlePending()方法進行pending處理. 如果pending不為空, 說明DirectBuffer被回收了, 就可以呼叫Cleaner的clean()進行回收了。
上面這個方法的程式碼在Reference類裡面,有興趣的同學可以去看一下那個方法的原始碼。
總結
以上就是Java中四種引用的區別。一般來說,強引用我們都知道,虛引用很少用到。而軟引用和弱引用的區別在於回收的時機:軟引用GC時,發現記憶體不夠才回收,弱引用只要一GC就會回收。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2704752/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 面試官:說說Java物件的四種引用方式面試Java物件
- Java的四種引用Java
- 美團一面:說一說Java中的四種引用型別?Java型別
- Java 四種引用的解讀Java
- Java中的四種引用方式(強引用、軟引用、弱引用、虛引用)Java
- Java四種引用包括強引用,軟引用,弱引用,虛引用。Java
- Java的四種引用和回收策略Java
- Java中的四種引用型別Java型別
- 詳聊Java的四種引用型別Java型別
- 終於有人把Java記憶體模型說清楚了Java記憶體模型
- 總算把執行緒六種狀態的轉換說清楚了!執行緒
- 【教程】終於有人把Java記憶體模型說清楚了!Java記憶體模型
- 安卓四種引用安卓
- Java Web(四) 一次性驗證碼的程式碼實現JavaWeb
- Java中強、軟、弱、虛四種引用詳解Java
- Java四種引用解析以及在Android的應用JavaAndroid
- 面試中的老大難-mysql事務和鎖,一次性講清楚!面試MySql
- 一次性講清楚「連線池獲取連線慢」的所有原因
- JAVA的字串這篇講清楚了Java字串
- 聽說這四個概念,很多 Java 老手都說不清Java
- C#:終於有人把 ValueTask、IValueTaskSource、ManualResetValueTaskSourceCore 說清楚了!C#
- css中圖片的四種地址引用CSS
- 把cookie聊清楚Cookie
- Java虛擬機器15:再談四種引用狀態Java虛擬機
- Java四大引用詳解:強引用、軟引用、弱引用、虛引用Java
- 說說TCP的三次握手和四次揮手TCP
- java的四大引用型別Java型別
- 一次性講清楚spring中bean的生命週期之一:getSingleton方法SpringBean
- 一次性講清楚spring中bean的生命週期之二:FactoryBean的前世今生SpringBean
- Kafka Network層解析,還是有人把它說清楚了Kafka
- Java設計模式之三種工廠模式 -- 總是忘記,這次把你記下來!Java設計模式
- 關於5G的原理,這張圖全說清楚了
- Java中的四大引用筆記Java筆記
- 這一次,徹底解決Java的值傳遞和引用傳遞Java
- java四大引用總結Java
- 一次性搞清Java中的類載入問題Java
- 理解類引用這種型別 (轉)型別
- 這一次,讓你徹底理解Java的值傳遞和引用傳遞!Java