JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

Java强软弱虚引用 java中强引用,弱引用,虚引用

wys521 2024-11-14 19:28:58 精选教程 26 ℃ 0 评论

Java中的四种引用

什么是引用

在各种编程语言中,都会存在一种“起名”的模式,就是说要为某一个值起个别名,就像每个人有一个名字一样,在Java中有两种类型,一种是基本类型,一种是引用类型。

Java中我们会使用变量来“起名”,比如基本类型的int i = 0,在内存中会开辟一小块内存存储着变量的值,i是变量的名字,0是变量的值。

还有另外一种类型,也就是引用,除了基本类型,其他都是引用类型,比如Object o = new Object(),对于这种形式的,我们就说o指向了一个对象地址,也可以说o引用了一个对象,程序中通过使用o这个引用来操作其底层真实引用的对象。

在Java中淡化了“指针”,引用其实就是就是一个功能受限但是更加安全的指针。

public static void main(String[] args) {
    Object o = new Object();
    System.out.println(o); // java.lang.Object@7cca494b
}

可以看到打印的内容是java.lang.Object@7cca494b,后面的数字其实就是内存的地址。

GC回收回调

finalize方法是Object中的方法,当对象被回收的时候,该方法会被调用,用户可以重写该方法,不过一般不建议重写,会引发性能、死锁等一系列问题,我们学习的时候可以重写。

finalize是JDK9以前的推荐方法,在JDK9以及以后的版本中,该方法已经被弃用,推荐使用java.lang.ref.Cleaner,使用方法比较简单,看后面的示例即可。

强引用

Java中,我们见到的最常见的引用就是强应用,当一个对象被分配给一个变量时,该变量就包含了对该对象的强引用。只要存在对该对象的强引用,垃圾收集器就不会回收该对象。

package cn.programtalk;

import java.lang.ref.Cleaner;

public class RefSample{

    static Cleaner cleaner = Cleaner.create();
    public static void main(String[] args) {
        RefSample rs = new RefSample();
        cleaner.register(rs, () -> {
            System.out.println("rs引用的对象被gc回收");
        });
        // rs = null;
        System.gc();
        System.out.println(rs); // cn.programtalk.RefSample@7699a589
    }
}

可以看到虽然执行System.gc()(执行gc),但是rs依然持有 new RefSample()的引用,也正验证了强应用的特点,取消注释再次执行,rs值打印为null,并且控制台输出”rs引用的对象被GC回收“,说明对象被GC回收了。

软引用

软引用是一种相对弱化的引用类型。当系统内存不足时,垃圾收集器会回收仅被软引用指向的对象。可以使用java.lang.ref.SoftReference类来创建软引用。

通过代码来演示下:

package cn.programtalk;

import java.lang.ref.Cleaner;
import java.lang.ref.SoftReference;

public class RefSample {

    static Cleaner cleaner = Cleaner.create();

    public static void main(String[] args) {
        SoftReference<byte[]> ref = new SoftReference<>(new byte[1024 * 1024 * 5]);
        cleaner.register(ref.get(), () -> {
            System.out.println("ref弱引用的对象被gc回收");
        });
        System.out.println(ref.get()); // ①
        System.gc();
        System.out.println(ref.get()); // ②
        byte[] bytes = new byte[1024 * 1024 * 5];
        System.out.println(ref.get()); // ③
    }
}

为了方便验证,我设置最大内存为10M(-Xmx10m)。

看下运行结果:

在①出打印出了对象信息,说明此时对象存在,在②处也打印出来了对象信息,说明对象依然没有被回收,在②之前使用了System.gc()为什么依然没有回收呢?这是因为,首先最大内存是10M,而软引用中的对象是5M,此时内存够用,所以没有回收。

接下来在③处打印了对象的是是null(在②之后我也并没有gc代码),说明对象被回收了,打印了"引用的对象被gc回收"也验证了对象确实被回收了,为什么此时就被回收了呢,这是因为程序又要创建byte[] bytes = new byte[1024 * 1024 * 5];5M的对象,内存最大是10M,此时内存使用过高,明显不足了,所以回收了弱引用的对象。

如果bytes的内存再大些,就会直接抛出堆内存不足的异常了,比如我修改bytes为10M,重新运行,结果如下:

软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

弱引用

弱引用比软引用更弱。当垃圾收集器运行时,无论当前内存是否充足,都会回收仅被弱引用指向的对象。可以使用java.lang.ref.WeakReference类来创建弱引用。

package cn.programtalk;

import java.lang.ref.Cleaner;
import java.lang.ref.WeakReference;

public class RefSample {

    static Cleaner cleaner = Cleaner.create();

    public static void main(String[] args) {
        WeakReference<RefSample> ref = new WeakReference<>(new RefSample());
        cleaner.register(ref.get(), () -> {
            System.out.println("引用的对象被gc回收");
        });
        System.gc();
        System.out.println(ref.get());
    }
}

运行结果如下:

可以看到发生gc就会将弱引用对象回收。

JDK中的ThreadLocal就使用了弱引用。

虚引用

PhantomReference(虚引用)是Java中最弱的一种引用类型。与其他几种引用类型不同,它不能单独使用,必须和引用队列(ReferenceQueue)联合使用。虚引用主要用于跟踪对象被垃圾收集器回收的活动,使用java.lang.ref.PhantomReference来创建虚引用。之前使用到的Cleaner就是通过虚引用实现的。

package cn.programtalk;

import java.lang.ref.*;

public class RefSample {

    public static void main(String[] args) throws InterruptedException {
        RefSample rs = new RefSample();
        ReferenceQueue<RefSample> refQueue = new ReferenceQueue<>();
        PhantomReference<RefSample> phantomRef = new PhantomReference<>(rs, refQueue);
        System.out.println(rs);
        rs = null;
        System.out.println("垃圾回收之前: " + phantomRef.get());
        System.gc();
        Thread.sleep(1000);
        System.out.println("垃圾回收之后: " + phantomRef.get());

        Reference<? extends RefSample> ref = refQueue.remove();
        if (ref != null) {
            System.out.println("对象被回收." +  ref.get());
        }
    }
}

打印结果如下:

垃圾回收前后,都无法获取对象,这是没错的,虚引用就是无法获取到具体的对象。

因为打印了”对象被回收。null“字样,说明确实将被回收的对象放入到了队列中。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表