java中int和integer的区别_Java中关于强、软、弱、虚引用的区别

前言

    在JDK 1.2版本之前,如果一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,对象的引用被分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。     JVM(Java Virtual Machine)是Java中提供的Java虚拟机,JVM负责内存的分配和回收,这是它的优点,不用像使用C语言,需要手动的释放内存。但同时JVM也是有它的缺点,就是不够灵活,为解决对内存操作不够灵活这个问题,可以采用软引用等方法。

具体描述

1. 强引用

     强引用是最普遍的引用,如果一个对象具有强引用,垃圾回收器绝不会回收它。当内存空间不足,JVM宁愿抛出OutOfMemoryError错误,让程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。     强引用就是像Person p = new Person(); 这种p就属于是一个强引用类型,如果p这个引用始终指向了new Person()这块内存的话,JVM则不会对这块内存进行回收。

示例

public class M {    @Override    protected void finalize() throws Throwable {        System.out.println("finalize");    }}public class T01_NormalReference {    public static void main(String[] args) {        M m = new M();        // m = null;        System.gc();        try {            System.in.read();        } catch (IOException e) {            e.printStackTrace();        }    }}
上例中,定义了个类重写了finalize()方法(默认情况下,当JVM准备释放掉一块内存之前,会先调用finalize()方法),所以当JVM准备回收内存时,就会执行我们重写的方法打印"finalize",下面定义了一个类,初始化了一个M类的对象,显式的调用一下gc()方法,让JVM准备回收内存。 如果根据上述强引用的概念,由于M m = new M();m的强引用并没有为空,它依然指向了new M()的这块内存区域,所以最后的执行结果并没有打印出"finalize"(没有调用finalize()方法),如果我们将m的引用设置为null,这时控制台就会打印出"finalize"字符串,因为强引用m不再指向实际的内存地址了,意为new M()没有强引用再指向它,所以JVM将内存回收。

最后的打印结果如下:

d6aeb3e0d2c09b4d6ba75921d48a8ec3.png

2. 软引用(SoftReference)

     软引用的概念是如果当前内存空间足够,垃圾回收器就不会回收它,如果当前内存不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以一直被程序使用。软引用可以用来实现高速缓存。

示例

public class T02_SoftReference {    public static void main(String[] args) {        SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);        System.out.println(m.get());        System.gc();        try {            Thread.sleep(500);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(m.get());        byte[] b = new byte[1024*1024*12];        System.out.println(m.get());    }    }
上例中,初始化了一个SoftReference软引用对象 m,并在其中设置了一个10M大小的byte[ ]数组,然后调用get()方法,判断该对象的内存是否被回收,如果被回收了调用get()方法就会得到null,接着调用gc()垃圾回收器,等待一会儿后再去调用get()方法,后面再初始化一个大小为12M的字节数组,再次调用get()方法,这个程序最后的执行结果如下:

b96f84e80e4e251a25f5b7c16d2b0e5b.png

程序中调用的3次get()都会得到结果,这是因为没有设置当前程序的最大内存空间,假设我们把最大内存设置成为20M的话,看下图:

c35c9cce2b3e0b6f761f906d9743cad0.png

最终的执行结果会是怎样的:

ff936b852522ff8fdf55cbeb00f90afa.png

可以看到,前两次调用get()方法都成功打印了,当调用第三次get()方法时,打印为空。这是因为当前环境的内存空间只有20M,开始软引用对象中创建的数组大小为10M,当第二次调用时,因为当前内存足够,所以不会回收对象内存,但下面又创建了一个大小为12M的数组,这时内存不够用了,JVM就会将之前的内存回收,所以最后调用m.get()方法得到的是null,因为m引用指向的对象所在的内存已经被回收了。

3. 弱引用(WeakReference)

     弱引用的概念是只有弱引用的对象会拥有更短暂的生命周期。在垃圾回收器线程扫描它的内存区域时,一旦发现了只具有弱引用的对象,不管当前内存空间是否足够,都会去回收它的内存。

示例

public class T03_WeakReference {    public static void main(String[] args) {        WeakReference m = new WeakReference<>(new M());        System.out.println(m.get());        System.gc();        System.out.println(m.get());    }}
上例中,实例化了一个WeakReference弱引用对象 m,为其设置了一个M对象,分别打印两次get()方法,在中间调用gc线程回收内存,执行结果如下:

e2fab5502aabfd7dd8141ed48bd1e80d.png

根据弱引用的概念我们可知,为什么第二次打印get()方法得到的值为null,这是因为垃圾回收器只要扫描到弱引用指向的对象,就会将该对象的内存空间回收,不管当前的内存空间是否充足。

4. 虚引用

虚引用和与其他的引用不大相同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用的作用主要是用来跟踪对象被垃圾回收的活动。 虚引用与软引用弱引用的区别就在于:虚引用必须要和ReferenceQueue引用队列联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到它所关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否要被垃圾回收了。

示例

public class T04_PhantomReference {    private static final List LIST = new LinkedList<>();    private static final ReferenceQueue QUEUE = new ReferenceQueue<>();    public static void main(String[] args) {        PhantomReference phantomReference = new PhantomReference<>(new M(), QUEUE);        new Thread(() -> {            while (true) {                LIST.add(new byte[1024 * 1024]);                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                    Thread.currentThread().interrupt();                }                System.out.println(phantomReference.get());            }        }).start();        new Thread(() -> {            while (true) {                Reference extends M> poll = QUEUE.poll();                if (poll != null) {                    System.out.println("--- 虚引用对象被Jvm回收了 ---" + poll);                }            }        }).start();        try {            Thread.sleep(500);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}// 执行结果nullfinalizenullnullnullnullnullnullnullnullnullnull--- 虚引用对象被Jvm回收了 ---java.lang.ref.PhantomReference@2831008dnullnullnullnullnullnullException in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space  at c_v5.T04_PhantomReference.lambda$main$0(T04_PhantomReference.java:20)  at c_v5.T04_PhantomReference$$Lambda$1/1078694789.run(Unknown Source)  at java.lang.Thread.run(Thread.java:748)
上例中,首先创建了一个List对象,创建了一个ReferenceQueue引用队列,实例化一个PhantomReference虚引用对象,里面放入一个对象,和一个ReferenceQueue队列,然后创建两个线程,线程1不断的往引用对象中放一个对象,线程2持续的对队列Poll,如果Poll不为空的时候,说明虚引用已经被jvm回收了。 最后的结果是,当每一次调用get()方法时,获取虚引用对象时发现所有的都是null,因为我们是没办法通过虚引用get出它所指向的对象的,程序直到内存到了我们指定的大小后,抛出OutOfMemoryError错误。 

虚引用主要用在什么地方?先来看一个图,如下:

0c8fcaf5df98acab2766db11129e97b3.png

首先看左边的这个DirectByteBuffer,它是NIO中提供的一种Buffer类型,叫做直接内存,直接内存它是不会被JVM虚拟机管理的,由操作系统管理(又称堆外内存),DirectByteBuffer是可以指向堆外内存的。

试想一下,如果DirectByteBuffer指向为null,那么堆外内存要怎么样回收呢?这种情况可以使用虚引用,当检测到ReferenceQueue队列中的虚引用被垃圾回收器回收时,就可以去堆外内存进去内存的回收了。

总结

在实际的程序设计中一般很少使用弱引用和虚引用,软引用的使用情况较多,因为软引用可以加速JVM对垃圾内存的回收速度,维护系统的运行安全,防止内存泄漏等问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值