Java:强引用、软引用、弱引用与虚引用

1. 概念

今天看到java强引用这些词,不会,没别的,学他就完事了!

1.1 背景与概念

JDK1.2版本以前:当一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及状态,程序才能使用它。简单来说:就是当我们一直在用那么它就一直有效,当我们不用了就会被回收掉,在想用就不可能了。

JDK 1.2版本开始,对象的引用被划分为 4种级别,这 4 种级别由高到低依次为:强引用、软引用、弱引用和虚引用。这样划分使程序能更加灵活地控制对象的生命周期,也可以更加方便我们去管理这些内存,同时可以让程序员通过代码的方式决定某些对象的生命周期。

1.2 垃圾回收机制

提到对象的声明周期,这必然与java的垃圾回收机制(GC)有关,所以我们先简单回顾一下GC:

GC是Java的一大特点,我们都知道c语言是不能自动释放内存的需要程序员去操作的,而Java自带的垃圾回收机制是能够帮助程序员自动释放内存的。

GC:是Java自带的垃圾回收器,它是运行在独立的、优先级比较低的线程中,时刻都在检测与释放无用的内存。 GC判断对象是否存活有两种方式,分别是引用计数法和引用链法(可达性分析法),主要用的较多的判断方式就是引用计数。

补充 引用计数概念:Java堆中给每个对象都有一个引用计数器,每当某个对象在其它地方被引用时,该对象的计数器 +1;引用失效则 -1;

2. 四种引用

2.1 强引用

定义

强引用是Java中的默认类型,当一个对象如果具有强引用,只要这种引用还存在,就不会被GC回收,甚至当内存不够时,就算抛出异常也不会对其进行回收。

以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用,例如下面的代码实现:

代码案例

public class Tests {
    
    
    public static void main(String[] args) {
    
    
        String str = new String("大家好,我是练习了两年半的强引用");
        System.out.println(str);
        List<String> strings = new ArrayList<>();
        strings.add(str);
        System.out.println(strings.toString());
    }
}

比如此处的str与strings中的数据就不会释放,即使内存不足他也会抛出错误,而不是清除数据。

因为强引用不能被回收,强引用可能导致强占空间不释放,积累的多了内存泄漏会导致内存溢,所以我们为了节约内存,在我们不用了之后可以采取措施帮助GC回收。

str=null;	//帮助垃圾收集器回收此对象,这样就能预防Java虚拟机抛出OutOfMemoryError错误

2.2 软引用

定义

如果一个对象具有软引用的,在JVM发生OOM(OutOfMemoryError)之前,(即内存充足的使用)是不会被GC,只有到JVM内存不足时,才会被GC。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。

代码案例

import java.lang.ref.SoftReference;
import java.util.*;

public class Tests {
    
    
    public static void main(String[] args) {
    
    
        String string = new String("大家好,我是练习了两年半的强引用");
        SoftReference<String> softReference = new SoftReference<>(string);
        System.out.println(string);
        System.out.println(softReference.get());
    }
}

image-20220727200608699

提到软引用就不能不提软引用在实际中重要的应用

例如浏览器的后退按钮:这个后退时显示的网页内容可以重新进行请求或者从缓存中取出

PS:当然没那么简单了,这只是一种策略,实际生活中这个后退时显示的网页内容是重新进行请求还是从缓存中取出 得看具体策略的:

(1)如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建
(2)如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出

2.3 弱引用

定义

如果一个对象只具有弱引用,那么这个对象就会被GC掉,被弱引用所引用的对象只能生存到下一次GC之前,当发生GC的时候无论当前内存是否足够,弱引用所引用的对象都会被GC掉。

弱引用还可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

代码案例

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class Tests {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        String string = new String("大家好,我是练习两年半的强引用");
        SoftReference<String> softReference=new SoftReference<String>(string); //软引用
        WeakReference<String> weakReference = new WeakReference<String>(string);//弱引用

        System.out.println("没gc======================");
		System.out.println(string);
		System.out.println(softReference.get());
        System.out.println(weakReference.get());
        System.gc();
        Thread.sleep(100000);
        System.out.println("gc了======================");
        System.out.println(string);
        System.out.println(softReference.get());
        System.out.println(weakReference.get());
    }
}

在使用软引用和弱引用的时候,我们可以显示地通过System.gc()来通知JVM进行垃圾回收,但是要注意的是,虽然发出了通知,JVM不一定会立刻执行,也就是说这句是无法确保此时JVM一定会进行垃圾回收的。

就像上面代码,等100s都没gc,结果还是有的

image-20220727201836540

注意这一点就好:该存储主要存储不是特别重要的数据,当我们内存空间不足时,GC会及时回收防止内存溢出。

2.4 虚引用

定义

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。简单来说其存在就是为了将关联虚引用的对象在被GC掉之后收到一个通知。

代码示例:

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class Tests {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        String string = new String("大家好,我是练习两年半的强引用");
        SoftReference<String> softReference=new SoftReference<String>(string); //软引用
        WeakReference<String> weakReference = new WeakReference<String>(string);//弱引用

        ReferenceQueue<Object> objectReferenceQueue = new ReferenceQueue<>();
        PhantomReference<String> phantomReference = new PhantomReference<>(string,objectReferenceQueue); // 虚引用

        System.out.println("没gc======================");
		System.out.println(string);
		System.out.println(softReference.get());
        System.out.println(weakReference.get());
        System.out.println(phantomReference.get());
        System.gc();
        System.out.println("gc了======================");
        System.out.println(string);
        System.out.println(softReference.get());
        System.out.println(weakReference.get());
        System.out.println(phantomReference.get());
    }
}

运行结果如下,可以看到是真的“虚”

image-20220727202220540

**注意:**在实际程序设计中一般很少使用弱引用与虚引用,使用软用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

3. 总结

1、Java四种引用级别:强引用、软引用、弱引用和虚引用。(由高到低)

垃圾回收器回收时,某些对象会被回收,某些不会被回收。垃圾回收器会从根对象 Object来标记存活的对象,然后将某些不可达的对象和一些引用的对象进行回收。

2、表格说明如下:

引用类型 被垃圾回收时间 用途 生存时间
强引用 从来不会 对象的一般状态 JVM停止运行时终止
软引用 当内存不足时 对象缓存 内存不足(OOM)时终止
弱引用 正常垃圾回收时 对象缓存 垃圾回收(GC)后终止
虚引用 任何时候都可能 跟踪对象的垃圾回收(因为有通知) 任何时候都可能终止

猜你喜欢

转载自blog.csdn.net/weixin_45525272/article/details/126022590