Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
869
2022-05-29
Java中的引用
前言
整體架構(gòu)
強(qiáng)引用
軟引用
弱引用
軟引用和弱引用的使用場(chǎng)景
WeakHashMap是什么?
虛引用
概念
場(chǎng)景
引用隊(duì)列 ReferenceQueue
GCRoots和四大引用小總結(jié)
前言
在原來的時(shí)候,我們談到一個(gè)類的實(shí)例化
Person p = new Person()
1
在等號(hào)的左邊,就是一個(gè)對(duì)象的引用,存儲(chǔ)在棧中
而等號(hào)右邊,就是實(shí)例化的對(duì)象,存儲(chǔ)在堆中
其實(shí)這樣的一個(gè)引用關(guān)系,就被稱為強(qiáng)引用
整體架構(gòu)
強(qiáng)引用
當(dāng)內(nèi)存不足的時(shí)候,JVM開始垃圾回收,對(duì)于強(qiáng)引用的對(duì)象,就算是出現(xiàn)了OOM也不會(huì)對(duì)該對(duì)象進(jìn)行回收,打死也不回收~!
強(qiáng)引用是我們最常見的普通對(duì)象引用,只要還有一個(gè)強(qiáng)引用指向一個(gè)對(duì)象,就能表明對(duì)象還“活著”,垃圾收集器不會(huì)碰這種對(duì)象。在Java中最常見的就是強(qiáng)引用,把一個(gè)對(duì)象賦給一個(gè)引用變量,這個(gè)引用變量就是一個(gè)強(qiáng)引用。當(dāng)一個(gè)對(duì)象被強(qiáng)引用變量引用時(shí),它處于可達(dá)狀態(tài),它是不可能被垃圾回收機(jī)制回收的,即使該對(duì)象以后永遠(yuǎn)都不會(huì)被用到,JVM也不會(huì)回收,因此強(qiáng)引用是造成Java內(nèi)存泄漏的主要原因之一。
對(duì)于一個(gè)普通的對(duì)象,如果沒有其它的引用關(guān)系,只要超過了引用的作用于或者顯示地將相應(yīng)(強(qiáng))引用賦值為null,一般可以認(rèn)為就是可以被垃圾收集的了(當(dāng)然具體回收時(shí)機(jī)還是要看垃圾回收策略)
強(qiáng)引用小例子:
/** * 強(qiáng)引用 * @author: 輕狂書生FS * @create: 2020-04-23-16:25 */ public class StrongReferenceDemo { public static void main(String[] args) { // 這樣定義的默認(rèn)就是強(qiáng)應(yīng)用 Object obj1 = new Object(); // 使用第二個(gè)引用,指向剛剛創(chuàng)建的Object對(duì)象 Object obj2 = obj1; // 置空 obj1 = null; // 垃圾回收 System.gc(); System.out.println(obj1); System.out.println(obj2); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
輸出結(jié)果我們能夠發(fā)現(xiàn),即使 obj1 被設(shè)置成了null,然后調(diào)用gc進(jìn)行回收,但是也沒有回收實(shí)例出來的對(duì)象,obj2還是能夠指向該地址,也就是說垃圾回收器,并沒有將該對(duì)象進(jìn)行垃圾回收
null java.lang.Object@14ae5a5
1
2
軟引用
軟引用是一種相對(duì)弱化了一些的引用,需要用Java.lang.ref.SoftReference類來實(shí)現(xiàn),可以讓對(duì)象豁免一些垃圾收集,對(duì)于只有軟引用的對(duì)象來講:
當(dāng)系統(tǒng)內(nèi)存充足時(shí),它不會(huì)被回收
當(dāng)系統(tǒng)內(nèi)存不足時(shí),它會(huì)被回收
軟引用通常在對(duì)內(nèi)存敏感的程序中,比如高速緩存就用到了軟引用,內(nèi)存夠用 的時(shí)候就保留,不夠用就回收
具體使用
/** * 軟引用 * * @author: 輕狂書生FS * @create: 2020-04-23-16:39 */ public class SoftReferenceDemo { /** * 內(nèi)存夠用的時(shí)候 */ public static void softRefMemoryEnough() { // 創(chuàng)建一個(gè)強(qiáng)應(yīng)用 Object o1 = new Object(); // 創(chuàng)建一個(gè)軟引用 SoftReference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
我們寫了兩個(gè)方法,一個(gè)是內(nèi)存夠用的時(shí)候,一個(gè)是內(nèi)存不夠用的時(shí)候
我們首先查看內(nèi)存夠用的時(shí)候,首先輸出的是 o1 和 軟引用的 softReference,我們都能夠看到值
然后我們把o1設(shè)置為null,執(zhí)行手動(dòng)GC后,我們發(fā)現(xiàn)softReference的值還存在,說明內(nèi)存充足的時(shí)候,軟引用的對(duì)象不會(huì)被回收
java.lang.Object@14ae5a5 java.lang.Object@14ae5a5 [GC (System.gc()) [PSYoungGen: 1396K->504K(1536K)] 1504K->732K(5632K), 0.0007842 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 504K->0K(1536K)] [ParOldGen: 228K->651K(4096K)] 732K->651K(5632K), [Metaspace: 3480K->3480K(1056768K)], 0.0058450 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] null java.lang.Object@14ae5a5
1
2
3
4
5
6
7
8
下面我們看當(dāng)內(nèi)存不夠的時(shí)候,我們使用了JVM啟動(dòng)參數(shù)配置,給初始化堆內(nèi)存為5M
-Xms5m -Xmx5m -XX:+PrintGCDetails
1
但是在創(chuàng)建對(duì)象的時(shí)候,我們創(chuàng)建了一個(gè)30M的大對(duì)象
// 創(chuàng)建30M的大對(duì)象 byte[] bytes = new byte[30 * 1024 * 1024];
1
2
這就必然會(huì)觸發(fā)垃圾回收機(jī)制,這也是中間出現(xiàn)的垃圾回收過程,最后看結(jié)果我們發(fā)現(xiàn),o1 和 softReference都被回收了,因此說明,軟引用在內(nèi)存不足的時(shí)候,會(huì)自動(dòng)回收
java.lang.Object@7f31245a java.lang.Object@7f31245a [GC (Allocation Failure) [PSYoungGen: 31K->160K(1536K)] 682K->811K(5632K), 0.0003603 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 160K->96K(1536K)] 811K->747K(5632K), 0.0006385 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 96K->0K(1536K)] [ParOldGen: 651K->646K(4096K)] 747K->646K(5632K), [Metaspace: 3488K->3488K(1056768K)], 0.0067976 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 646K->646K(5632K), 0.0004024 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 646K->627K(4096K)] 646K->627K(5632K), [Metaspace: 3488K->3488K(1056768K)], 0.0065506 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] null null
1
2
3
4
5
6
7
8
9
10
11
弱引用
不管內(nèi)存是否夠,只要有GC操作就會(huì)進(jìn)行回收
弱引用需要用 java.lang.ref.WeakReference 類來實(shí)現(xiàn),它比軟引用生存期更短
對(duì)于只有弱引用的對(duì)象來說,只要垃圾回收機(jī)制一運(yùn)行,不管JVM的內(nèi)存空間是否足夠,都會(huì)回收該對(duì)象占用的空間。
/** * 弱引用 * * @author: 輕狂書生FS * @create: 2020-04-24-10:18 */ public class WeakReferenceDemo { public static void main(String[] args) { Object o1 = new Object(); WeakReference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
我們看結(jié)果,能夠發(fā)現(xiàn),我們并沒有制造出OOM內(nèi)存溢出,而只是調(diào)用了一下GC操作,垃圾回收就把它給收集了
java.lang.Object@14ae5a5 java.lang.Object@14ae5a5 [GC (System.gc()) [PSYoungGen: 5246K->808K(76288K)] 5246K->816K(251392K), 0.0008236 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 808K->0K(76288K)] [ParOldGen: 8K->675K(175104K)] 816K->675K(251392K), [Metaspace: 3494K->3494K(1056768K)], 0.0035953 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] null null
1
2
3
4
5
6
7
8
軟引用和弱引用的使用場(chǎng)景
場(chǎng)景:假如有一個(gè)應(yīng)用需要讀取大量的本地圖片
如果每次讀取圖片都從硬盤讀取則會(huì)嚴(yán)重影響性能
如果一次性全部加載到內(nèi)存中,又可能造成內(nèi)存溢出
此時(shí)使用軟引用可以解決這個(gè)問題
設(shè)計(jì)思路:使用HashMap來保存圖片的路徑和相應(yīng)圖片對(duì)象關(guān)聯(lián)的軟引用之間的映射關(guān)系,在內(nèi)存不足時(shí),JVM會(huì)自動(dòng)回收這些緩存圖片對(duì)象所占的空間,從而有效地避免了OOM的問題
Map
1
WeakHashMap是什么?
比如一些常常和底層打交道的,mybatis等,底層都應(yīng)用到了WeakHashMap
WeakHashMap和HashMap類似,只不過它的Key是使用了弱引用的,也就是說,當(dāng)執(zhí)行GC的時(shí)候,HashMap中的key會(huì)進(jìn)行回收,下面我們使用例子來測(cè)試一下
我們使用了兩個(gè)方法,一個(gè)是普通的HashMap方法
我們輸入一個(gè)Key-Value鍵值對(duì),然后讓它的key置空,然后在查看結(jié)果
private static void myHashMap() { Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
第二個(gè)是使用了WeakHashMap,完整代碼如下
/** * WeakHashMap * @author: 陌溪 * @create: 2020-03-24-11:33 */ public class WeakHashMapDemo { public static void main(String[] args) { myHashMap(); System.out.println("=========="); myWeakHashMap(); } private static void myHashMap() { Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
最后輸出結(jié)果為:
{1=HashMap} {1=HashMap} ========== {1=WeakHashMap} {}
1
2
3
4
5
從這里我們看到,對(duì)于普通的HashMap來說,key置空并不會(huì)影響,HashMap的鍵值對(duì),因?yàn)檫@個(gè)屬于強(qiáng)引用,不會(huì)被垃圾回收。
但是WeakHashMap,在進(jìn)行GC操作后,弱引用的就會(huì)被回收
虛引用
概念
虛引用又稱為幽靈引用,需要java.lang.ref.PhantomReference 類來實(shí)現(xiàn)
顧名思義,就是形同虛設(shè),與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期。
如果一個(gè)對(duì)象持有虛引用,那么它就和沒有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收,它不能單獨(dú)使用也不能通過它訪問對(duì)象,虛引用必須和引用隊(duì)列ReferenceQueue聯(lián)合使用。
虛引用的主要作用和跟蹤對(duì)象被垃圾回收的狀態(tài),僅僅是提供一種確保對(duì)象被finalize以后,做某些事情的機(jī)制。
PhantomReference的get方法總是返回null,因此無法訪問對(duì)象的引用對(duì)象。其意義在于說明一個(gè)對(duì)象已經(jīng)進(jìn)入finalization階段,可以被gc回收,用來實(shí)現(xiàn)比finalization機(jī)制更靈活的回收操作
換句話說,設(shè)置虛引用關(guān)聯(lián)的唯一目的,就是在這個(gè)對(duì)象被收集器回收的時(shí)候,收到一個(gè)系統(tǒng)通知或者后續(xù)添加進(jìn)一步的處理,Java技術(shù)允許使用finalize()方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前,做必要的清理工作
這個(gè)就相當(dāng)于Spring AOP里面的后置通知
場(chǎng)景
一般用于在回收時(shí)候做通知相關(guān)操作
引用隊(duì)列 ReferenceQueue
軟引用,弱引用,虛引用在回收之前,需要在引用隊(duì)列保存一下
我們?cè)诔跏蓟娜跻没蛘咛撘玫臅r(shí)候,可以傳入一個(gè)引用隊(duì)列
Object o1 = new Object(); // 創(chuàng)建引用隊(duì)列 ReferenceQueue
1
2
3
4
5
6
7
那么在進(jìn)行GC回收的時(shí)候,弱引用和虛引用的對(duì)象都會(huì)被回收,但是在回收之前,它會(huì)被送至引用隊(duì)列中
完整代碼如下:
/** * 虛引用 * * @author: 輕狂書生FS * @create: 2020-04-24-12:09 */ public class PhantomReferenceDemo { public static void main(String[] args) { Object o1 = new Object(); // 創(chuàng)建引用隊(duì)列 ReferenceQueue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
運(yùn)行結(jié)果
java.lang.Object@14ae5a5 java.lang.Object@14ae5a5 null 執(zhí)行GC操作 null null java.lang.ref.WeakReference@7f3124
1
2
3
4
5
6
7
從這里我們能看到,在進(jìn)行垃圾回收后,我們?nèi)跻脤?duì)象,也被設(shè)置成null,但是在隊(duì)列中還能夠?qū)С鲈撘玫膶?shí)例,這就說明在回收之前,該弱引用的實(shí)例被放置引用隊(duì)列中了,我們可以通過引用隊(duì)列進(jìn)行一些后置操作
GCRoots和四大引用小總結(jié)
紅色部分在垃圾回收之外,也就是強(qiáng)引用的
藍(lán)色部分:屬于軟引用,在內(nèi)存不夠的時(shí)候,才回收
虛引用和弱引用:每次垃圾回收的時(shí)候,都會(huì)被干掉,但是它在干掉之前還會(huì)存在引用隊(duì)列中,我們可以通過引用隊(duì)列進(jìn)行一些通知機(jī)制
Java JVM
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。