Java引用类型

2022-07-26 17:04:19 浏览数 (1)

前言

使用Java开发,我们不需要去管理对象的生命周期,因为JVM会帮我们回收垃圾,不过这就是安全的吗,显然不是,因为JVM 的堆区存在了很多未回收的对象实例,那么就有可能发生内存溢出,所以我们就有必要在对强引用,弱引用,软引用,虚引用 有所了解。

强引用

我们可以这样理解强引用,我们家中有什么必需品?我觉得像床,衣柜这些东西是必需品,就算只有20平米的房间,我们依然需要, 那么强引用就可以看成是床,衣柜,我们在开发的时候,会创建很多对象实例,大部分都是强引用,强引用不会被JVM回收,即使 发生了OutOfMemory也不会回收,除非我们将其设置为null才会回收。

代码语言:javascript复制
public class StrongReference {
    public static void main(String[] args) {
        User user = new User("steak");
        System.out.println(user);
        System.gc();
        System.out.println(user);
    }
}
代码语言:javascript复制
User(username=steak)
User(username=steak)

上面创建的user就是一个强引用,我们使用System.gc()手动回收,user也没被回收,我们需要将user设值为null, JVM才会回收。

代码语言:javascript复制
public class StrongReference {
    public static void main(String[] args) {
        User user = new User("steak");
        System.out.println(user);
        user = null;
        System.gc();
        System.out.println(user);
    }
}
代码语言:javascript复制
User(username=steak)
null

软引用

软引用可以理解为家里的非必需品,但是是建立在家里空间很足的情况下,如果家里空间很足,那么就不会丢掉,如果家里空间不足了, 就可以丢弃,就好比一些已经不常用的老桌子,家里空间足的时候,可以留着它,但是空间不足了,就可以把它丢了,在JVM中,如果内存空间 充足,那么就不会回收软引用,如果内存空间不足了,就会回收掉。

代码语言:javascript复制
public class SoftReferenceTest {
    public static void main(String[] args) {
        SoftReference<Object> softReference = new SoftReference<>(new User("steak"));
        System.out.println(softReference.get());
        System.gc();
        System.out.println(softReference.get());
    }
}
代码语言:javascript复制
User(username=steak)
User(username=steak)

如上因为空间是充足的,所以user没被回收。

软引用应用例子

比如我们需要读取大量的图片到内存中来,以提高查询的速度,如果每次每次读取都从文件服务器读取,那么性能会有很大的影响, 所以这里就存放在内存中,但是由于存放大量的图片在内存中,可能会导致内存溢出,所以使用软引用来解决这个问题,当内存不足的 时候,就会将其回收掉,内存充足时,就不会回收,这时可以直接从内存中回去,大大的提高了查询的速度。

代码语言:javascript复制
public class ImagesCache {
    private final Map<String, SoftReference<ImageInfo>> map = new HashMap<>();

    public void add(ImageInfo imageInfo){
        if (imageInfo != null){
            SoftReference<ImageInfo> softReferenceImage = new SoftReference<ImageInfo>(imageInfo);
            map.put(imageInfo.getUrl(),softReferenceImage);
        }
    }

    public ImageInfo get(String url){
        SoftReference<ImageInfo> img = map.get(url);
        if (img == null){
            System.out.println("image does not exist");
            return null;
        }
       return img.get();
    }
}

如上定义了一个Map来存放图片,key为图片url,value为图片对象的软引用。

弱引用

弱引用相对于软引用来说,它不会等到内存不足才被回收,而是只要JVM进行垃圾回收,都会对它进行回收。

代码语言:javascript复制
public class WeakReferenceTest {
    public static void main(String[] args) {
        WeakReference<User> userWeakReference = new WeakReference<>(new User("steak"));
        System.out.println(userWeakReference.get());
        System.gc();
        System.out.println(userWeakReference.get());
    }
}
代码语言:javascript复制
User(username=steak)
null

如上我们System.gc()进行手动回收,发现user引用被回收了。

注意:需要注意的是,如果有一个强引用和弱引用关联了,那么这个弱引用不会被回收。
代码语言:javascript复制
public class WeakReferenceTest {
    public static void main(String[] args) {
        WeakReference<User> userWeakReference = new WeakReference<>(new User("steak"));
        Object b = userWeakReference.get();
        System.gc();
        System.out.println(userWeakReference.get());
        System.out.println(b);
    }
}
代码语言:javascript复制
User(username=steak)
User(username=steak)
User(username=steak)

如上将弱引用userWeakReference赋给了Object b,那么userWeakReference和b之间就关联了起来,所以弱引用就 无法被JVM回收。

我们将b设置为null后,就可以被回收

代码语言:javascript复制
public class WeakReferenceTest {
    public static void main(String[] args) {
        WeakReference<User> userWeakReference = new WeakReference<>(new User("steak"));
        Object b = userWeakReference.get();
        System.out.println(userWeakReference.get());
        b = null;
        System.gc();
        System.out.println(userWeakReference.get());
        System.out.println(b);
    }
}
代码语言:javascript复制
User(username=steak)
null
null

虚引用

虚引用和其他的引用不一样,它随时都可能被回收,虚引用需要和引用队列ReferenceQueue一起来使用。

代码语言:javascript复制
public class PhantomReferenceTest {
    public static void main(String[] args) {
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<User> phantomReference = new PhantomReference<>(new User("123"),referenceQueue);
        System.out.println(phantomReference.get());
    }
}
代码语言:javascript复制
null
null

从输出可以看出虚引用被回收了。

总结

从上面的强引用,软引用,弱引用,虚引用可以得出,强引用是我们使用得最多的,它是不会被回收的,即使发生了OOM,软引用会在内存不足的情况下被回收, 弱引用只要JVM进行垃圾回收,它都会被回收,虚引用则随时被回收。

0 人点赞