当前位置: 首页 > news >正文

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对垃圾内存的回收速度,维护系统的运行安全,防止内存泄漏等问题。

相关文章:

  • android 投屏_[Android] 虫洞手机投屏电脑(支持键盘映射和传声音)
  • 云丁智能锁说明书_真硬核!行业爆发前夜,这把锁登上航母
  • python调用node_node-python:在nodejs中调用python代码
  • python多线程读取文件内容_python多线程读取logcat内容,导致其他线程阻塞
  • python闭包满足的三个条件_Python中的闭包
  • javascript等待异步线程完成_程序员修神之路--问世间异步为何物?
  • python取随机数画图_python3测试工具开发快速入门教程1turtle绘图-4选择与随机数...
  • python中如何输入多行字符_python中怎么输入多行字符串
  • python饼图显示百分比_解决echarts饼图显示百分比,和显示内容字体及大小
  • java 二维数组定义长方体_47.二维数组的定义
  • zap 自定义日志格式_Go 每日一库之 zap
  • python接管已经打开ie浏览器_Python Webdriver 重新使用已经打开的浏览器实例
  • python 单例 多线程_python 单例模式
  • echarts绘制大数量折线图导致浏览器崩溃_现代浏览器内部机制
  • filestream读取文件_ASP.NET Core WebAPI文件下载
  • Date型的使用
  • gulp 教程
  • Java方法详解
  • KMP算法及优化
  • mysql外键的使用
  • Octave 入门
  • Python语法速览与机器学习开发环境搭建
  • Vue2.0 实现互斥
  • 大快搜索数据爬虫技术实例安装教学篇
  • 来,膜拜下android roadmap,强大的执行力
  • 聊聊hikari连接池的leakDetectionThreshold
  • puppet连载22:define用法
  • 我们雇佣了一只大猴子...
  • # 达梦数据库知识点
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • ${factoryList }后面有空格不影响
  • (function(){})()的分步解析
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (ZT)一个美国文科博士的YardLife
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (一)u-boot-nand.bin的下载
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)visual stdio 书签功能介绍
  • ../depcomp: line 571: exec: g++: not found
  • .net Stream篇(六)
  • .net 反编译_.net反编译的相关问题
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .set 数据导入matlab,设置变量导入选项 - MATLAB setvaropts - MathWorks 中国
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?
  • [2024最新教程]地表最强AGI:Claude 3注册账号/登录账号/访问方法,小白教程包教包会
  • [android] 请求码和结果码的作用
  • [Android]竖直滑动选择器WheelView的实现
  • [Angular 基础] - 表单:响应式表单
  • [Angularjs]ng-select和ng-options
  • [AR]Vumark(下一代条形码)
  • [BZOJ3757] 苹果树
  • [BZOJ4010]菜肴制作