詳解Java的四種引用——強軟弱虛,顛覆你的認知!


強軟弱虛

java中的數據被類型分為了兩類,它們分別是基本類型和引用類型。一般我們new出來的對象都屬於引用類型的范疇。我們知道java是有垃圾回收機制的一種語言,根據垃圾回收時的策略,java將對於堆對象的引用又進行了細分,引用被分為了強引用,軟引用,弱引用和虛引用。

強引用

強引用又稱普通引用,它是最常見的一種引用類型,一般我們通過new關鍵字創建對象時,變量對於堆對象的引用就是強引用。
在這里插入圖片描述
強引用的特點:

  • 如果堆中的一個對象被強引用指向,那么這個變量將不會被GC回收。
  • 在堆內存不夠用的情況下,被強引用指向的對象也不會被回收。(寧可拋出OOM異常)
  • 被強引用指向的對象,只有在引用消失后才會被GC回收。

測試代碼-1:

 1 /* 這個類用於申請堆內存 */
 2 public class Memory {
 3     private byte[] alloc;
 4 
 5     public Memory(int size) { this.alloc = new byte[size]; }
 6 
 7     @Override
 8     protected void finalize() throws Throwable {
 9         super.finalize();
10         System.out.println("被GC回收");
11     }
12 }
 1 public class Normal {
 2     public static void main(String[] args) throws InterruptedException {
 3 
 4         /* 棧變量m對new出來的Memory對象的引用為強引用 */
 5         Memory m = new Memory(1024 * 1024 * 20);
 6 
 7         /* 現在堆中的對象沒有引用指向它,它要被GC回收 */
 8         m = null;
 9 
10         System.gc(); /* 顯式GC */
11 
12         /*
13          * 當我們啟動java程序時,默認會有兩個線程,一個是我們的主線程,另一個便是GC線程。
14          * 通常GC線程的優先級比較低,並且GC線程默認為守護線程,即它會在主線程退出的同
15             * 時退出。
16          *
17          * 為了觀察到GC的效果,我們讓主線程休眠1s
18          */
19         Thread.sleep(1000);
20     }
21 
22 }
 
         
         
        

測試結果-1:
在這里插入圖片描述
測試代碼-2

 1 public class Normal {
 2     
 3     public static void main(String[] args) throws InterruptedException {
 4 
 5         /* 我們設定JVM參數,設置堆內存大小為25MB */
 6 
 7 
 8         /* 棧變量m1對new出來的Memory對象的引用為強引用 */
 9 
10         /* 申請了20MB的內存,實際會大於20MB,因為我們的byte[]被Memory對象wrapper */
11         Memory m1 = new Memory(1024 * 1024 * 20);
12         
13         System.gc();
14         Thread.sleep(1000);
15 
16         /* 再申請10MB堆內存 */
17         Memory m2 = new Memory(1024 * 1024 * 10);
18     }
19 
20 }
 

測試結果-2
在這里插入圖片描述

軟引用

軟引用的創建需要借助jdk中java.lang.ref.SoftReference這個類來創建。也就是說,我們的變量是先引用到SoftReference這個對象,SofReference這個對象再去引用我們想要設置為軟引用的對象。
在這里插入圖片描述
軟引用的特點

  • 當堆內存夠用時,被軟引用指向的對象不會被GC回收。
  • 當堆內存不夠用時,被軟引用指向的對象自動的被GC回收。

測試代碼-3

 1 public class Soft_Ref {
 2 
 3     public static void main(String[] args) throws InterruptedException {
 4         /* 堆內存大小為50MB */
 5         /* 申請30MB */
 6         SoftReference<Memory> m1 = new SoftReference<>(new Memory(1024 * 1024 * 30));
 7 
 8         System.gc(); /* 顯示調用GC */
 9 
10         /* 此時內存夠用,所以結果可以預見性的為GC不會回收被軟引用指向的對象 */
11         
12         Thread.sleep(1000);
13     }
14 }
 

測試結果-3
在這里插入圖片描述
測試代碼-4

 1 public class Soft_Ref {
 2     public static void main(String[] args) throws InterruptedException {
 3 
 4         /* 堆內存大小為50MB */
 5 
 6         /* 申請30MB */
 7         SoftReference<Memory> m1 = new SoftReference<>(new Memory(1024 * 1024 * 30));
 8 
 9         
10         /* 申請20MB */
11         for (int i = 0; i < 20; ++i) {
12             System.out.println("[time] => " + System.currentTimeMillis());
13             SoftReference<Memory> ma = new SoftReference<>(new Memory(1024 * 1024));
14             Thread.sleep(200);
15         }
16     }
17 }
 

測試結果-4
在這里插入圖片描述

從測試結果可以看出,當內存不夠用或者將要不夠用時,會觸發GC,GC會自動的回收那些軟引用指向對象。

一定要注意,軟引用指向對象的回收是在觸發GC的條件下才會被回收,如果內存夠用,就算顯式的調用GC,軟引用指向的對象也不會被回收。

弱引用

弱引用的創建方式與軟引用類似,需要借助於jdk中java.lang.ref.WeakReference類去創建。
在這里插入圖片描述
弱引用的特點:

  • 不管什么情況,遇到GC就會回收被弱引用指向的對象。

測試代碼-5

1 public class Weak_Ref {
2 
3     public static void main(String[] args) throws InterruptedException {
4         /* 堆內存沒有設置大小,為默認狀態 */
5         WeakReference<Memory> m = new WeakReference<>(new Memory(1024 * 1024 * 10));
6         System.gc(); /* 調用GC */
7         Thread.sleep(1000);
8     }
9 }
 
測試結果-5
在這里插入圖片描述

虛引用

虛引用是一種十分特殊的引用,它主要用在堆外內存的管理,虛引用可以指向堆中的對象,但是沒有實際的意義。
在這里插入圖片描述
在這里插入圖片描述
虛引用的特點:

  • 無法獲取虛引用指向的對象的值。
  • 虛引用在被GC回收時會有通知。
  • 虛引用在遇到GC時,不管是否還有對象引用它,它都會被GC回收。

測試代碼-6

 1 public class Phantom_Ref {
 2     static final ArrayList<byte[]> LIST = new ArrayList<>();
 3 
 4     static final ReferenceQueue<Memory> QUEUE = new ReferenceQueue<>();
 5 
 6     public static void main(String[] args) throws InterruptedException {
 7 
 8         PhantomReference<Memory> m = new PhantomReference<>(new Memory(1024 * 1024 * 10), QUEUE);
 9         new Thread(()->{
10             while (true) {
11                 LIST.add(new byte[1024 * 1024 ]);
12 
13                 try {
14                     Thread.sleep(1000);
15                 } catch (InterruptedException e) {
16                     e.printStackTrace();
17                     Thread.currentThread().interrupt();
18                 }
19                 System.out.println(m.get()); /* 虛引用指向的值永遠無法被獲取 */
20             }
21         }).start();
22 
23         new Thread(()->{
24             while (true) {
25                 Reference<? extends Memory> poll = QUEUE.poll();
26                 if (poll != null) {
27                     /* 虛引用在對象回收時,會進行通知 */
28                     System.out.println("有虛引用被GC回收了-" + poll);
29                     break;
30                 }
31             }
32         }).start();
33     }
34 }
 

測試結果-6
在這里插入圖片描述

以上就是小編整理的Java的四種引用,只是個人的一些理解看法,有哪里不准確的地方,還請大家多多指出,小編和大家共同進步!!!

喜歡文章請多多點贊評論轉發,關注小編,你們的支持就是小編創作的無限動力!!!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM