前言
總所周知, java不同于c/c++臭墨,它不需要程序員自已來管理內(nèi)存(分配赔嚎,釋放內(nèi)存),java 會自己來管理內(nèi)存,比如銷毀某些不再被使用的對象尤误。這些操作都是在一個后臺線程默默進行(Garbage Collector Thread)侠畔,也就是垃圾收集器線程,根據(jù)jvm實現(xiàn)的策略來釋放對象內(nèi)存损晤。但是程序編寫者卻無法控制這個后臺線程软棺,無法讓它在你想要的時候開始釋放內(nèi)存,銷毀對象尤勋,按照你的規(guī)定來銷毀那些對象喘落,釋放內(nèi)存,這些都是都jvm自己控制的最冰。但是隨著 java.lang.ref這個包下的類的引進瘦棋,程序員擁有了一點點控制你創(chuàng)建的對象何時釋放,銷毀的權(quán)利暖哨,當(dāng)然只是一點點赌朋。那接下來就來看看這些類和java中的四種引用類型有何對應(yīng)關(guān)系。先來看看這四種引用類型篇裁,因為jvm在進行垃圾收集的時候沛慢,需要判斷哪些對象需要銷毀,這時就與這幾種引用類型相關(guān)茴恰。
以下是這幾種引用類型:
- 強引用(Strong References)
強引用類型是我們平時寫代碼的時候最常用的引用颠焦,而大部分人往往都會忽略這個概念斩熊,都成一種理所當(dāng)然的事情了往枣。
接下來看看下面這個簡單的例子:
public class Main {
public static void main(String[] args) {
//創(chuàng)建一個對象,new出來的對象都是分配在java堆中的
Sample sample = new Sample(); //sample這個引用就是強引用
sample = null; //將這個引用指向空指針,
//那么上面那個剛new來的對象就沒用任何其它有效的引用指向它了
//也就說該對象對于垃圾收集器是符合條件的
//因此在接下來某個時間點 GC進行收集動作的時候, 該對象將會被銷毀粉渠,內(nèi)存被釋放
}
}
class Sample {
}
也可以畫個簡單的圖理解一下:
- 軟引用(Soft References)
軟引用在java.lang.ref包中有與之對應(yīng)的類java.lang.ref.SoftReference分冈。
重點: 被弱引用指向的對象不會被垃圾收集器收集(即使該對象沒有強引用指向它),除非jvm使用內(nèi)存不夠了霸株,才會對這類對象進行銷毀雕沉,釋放內(nèi)存。舉個簡單的例子:
public class Main {
public static void main(String[] args) {
//創(chuàng)建一個對象去件,new出來的對象都是分配在java堆中的
Sample sample = new Sample(); //sample這個引用就是強引用
//創(chuàng)建一個軟引用指向這個對象 那么此時就有兩個引用指向Sample對象
SoftReference<Sample> softRef = new SoftReference<Sample>(sample);
//將強引用指向空指針 那么此時只有一個軟引用指向Sample對象
//注意:softRef這個引用也是強引用坡椒,它是指向SoftReference這個對象的
//那么這個軟引用在哪呢? 可以跟一下java.lang.Reference的源碼
//private T referent; 這個才是軟引用尤溜, 只被jvm使用
sample = null;
//可以重新獲得Sample對象倔叼,并用一個強引用指向它
sample = softRef.get();
}
}
class Sample {
}
有興趣可以去看看Reference的源碼。
現(xiàn)在是不是想到了軟引用的一個使用場景宫莱,它相比與強引用可以避免OOM丈攒。
現(xiàn)在可以簡單的測試下 當(dāng)jvm內(nèi)存不足的情況下,軟引用的回收情況。
為了更快的看到結(jié)果巡验,我限制了jvm的最大堆內(nèi)存 -Xmx100m 為100m
public class Main {
private static final List<Object> TEST_DATA = new LinkedList<>();
public static void main(String[] args) throws InterruptedException {
//創(chuàng)建一個對象际插,new出來的對象都是分配在java堆中的
Sample sample = new Sample(); //sample這個引用就是強引用
//創(chuàng)建一個軟引用指向這個對象 那么此時就有兩個引用指向Sample對象
SoftReference<Sample> softRef = new SoftReference<Sample>(sample);
//將強引用指向空指針 那么此時只有一個軟引用指向Sample對象
//注意:softRef這個引用也是強引用,它是指向SoftReference這個對象的
//那么這個軟引用在哪呢显设? 可以跟一下java.lang.Reference的源碼
//private T referent; 這個才是軟引用框弛, 只被jvm使用
sample = null;
//可以重新獲得Sample對象,并用一個強引用指向它
//sample = softRef.get();
new Thread(){
@Override
public void run() {
while (true) {
System.out.println(softRef.get());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
TEST_DATA.add(new byte[1024 * 1024 * 5]);
}
}
}.start();
Thread.currentThread().join();
}
}
class Sample {
private final byte[] data;
public Sample() {
data = new byte[1024 * 1024 * 10];
}
}
可以看到當(dāng)jvm內(nèi)存耗盡的時候捕捂,會將弱引用的對象進行回收 上面的例子的10m功咒,且剛好還可以分配兩次 5m一次 ,輸出了兩次null也證明了這一點
當(dāng)然我們在創(chuàng)建軟引用時绞蹦,還可以傳入ReferenceQueue力奋,這個隊列有啥用呢? 當(dāng)jvm回收某個軟引用對象之后會將該SoftReference對象(例子中的softRef對象)添加進這個隊列幽七,因此我們就知道這個對象啥時候被回收了景殷,可以做一些我們想做的操作。
- 弱引用(Weak References)
弱引用會被jvm忽略澡屡,也就說在GC進行垃圾收集的時候猿挚,如果一個對象只有弱引用指向它,那么和沒有引用指向它是一樣的效果驶鹉,jvm都會對它就行果斷的銷毀绩蜻,釋放內(nèi)存。其實這個特性是很有用的室埋,jdk也提供了java.util.WeakHashMap這么一個key為弱引用的Map办绝。比如某個資源對象你要釋放(比如 db connection), 但是如果被其它map作為key強引用了,就無法釋放姚淆,被jvm收集孕蝉。
來個簡單的例子:
public class Main {
private static final List<Object> TEST_DATA = new LinkedList<>();
private static final ReferenceQueue<Sample> QUEUE = new ReferenceQueue<>();
public static void main(String[] args) {
//創(chuàng)建一個對象,new出來的對象都是分配在java堆中的
Sample sample = new Sample(); //sample這個引用就是強引用
//創(chuàng)建一個弱引用指向這個對象 那么此時就有兩個引用指向Sample對象
//SoftReference<Sample> softRef = new SoftReference<Sample>(sample, QUEUE);
WeakReference<Sample> weakRef = new WeakReference<Sample>(sample, QUEUE);
//將強引用指向空指針 那么此時只有一個弱引用指向Sample對象
//注意:softRef這個引用也是強引用腌逢,它是指向SoftReference這個對象的
//那么這個弱引用在哪呢降淮? 可以跟一下java.lang.Reference的源碼
//private T referent; 這個才是弱引用, 只被jvm使用
sample = null;
//可以重新獲得Sample對象搏讶,并用一個強引用指向它
//sample = softRef.get();
new Thread(){
@Override
public void run() {
while (true) {
System.out.println(weakRef.get());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
TEST_DATA.add(new byte[1024 * 1024 * 5]);
}
}
}.start();
new Thread(){
@Override
public void run() {
while (true) {
Reference<? extends Sample> poll = QUEUE.poll();
if (poll != null) {
System.out.println("--- 弱引用對象被jvm回收了 ---- " + poll);
System.out.println("--- 回收對象 ---- " + poll.get());
}
}
}
}.start();
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
System.exit(1);
}
}
}
class Sample {
private final byte[] data;
public Sample() {
data = new byte[1024 * 1024 * 10];
}
}
- 虛幻引用(Phantom References)
虛幻應(yīng)用和弱引用的回收機制差不多佳鳖,都是可以被隨時回收的。但是不同的地方是媒惕,它的構(gòu)造方法必須強制傳入ReferenceQueue系吩,因為在jvm回收前(重點: 對,就是回收前吓笙,軟引用和弱引用都是回收后)淑玫,會將PhantomReference對象加入ReferenceQueue中; 還有一點就是PhantomReference.get()方法永遠返回空,不管對象有沒有被回收。