详解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的四种引用,只是个人的一些理解看法,有哪里不准确的地方,还请大家多多指出,小编和大家共同进步!!!

喜欢文章请多多点赞评论转发,关注小编,你们的支持就是小编创作的无限动力!!!!!

posted @ 2020-09-11 17:06  Java领域指导者  阅读(752)  评论(0编辑  收藏  举报