java中的强,软,弱,虚引用
引用的应用场景
- 我们都知道垃圾回收器会回收符合回收条件的对象的内存,但并不是所有的程序员都知道回收条件取决于指向该对象的引用类型。这正是Java中弱引用和软引用的主要区别。
- 如果一个对象只有弱引用指向它,垃圾回收器会立即回收该对象,这是一种急切回收方式。
- 相对的,如果有软引用指向这些对象,则只有在JVM需要内存时才回收这些对象。
- 弱引用和软引用的特殊行为使得它们在某些情况下非常有用。
- 例如:软引用可以很好的用来实现缓存,当JVM需要内存时,垃圾回收器就会回收这些只有被软引用指向的对象。
- 而弱引用非常适合存储元数据,例如:存储ClassLoader引用。如果没有类被加载,那么也没有指向ClassLoader的引用。一旦上一次的强引用被去除,只有弱引用的ClassLoader就会被回收。
强引用(StrongReference)
- 强引用是我们在编程过程中使用的最简单的引用,如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用。
- 任何被强引用指向的对象都不能被垃圾回收器回收,这些对象都是在程序中需要的。
- StringBuffer stringBuffer = new StringBuffer(); 此时的object也是一个强引用。我们在开发过程中使用的最多的就是强引用。这个就不做过多的说明了。
软引用(SoftReference)
软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可使用该对象;当系统内存空间不足时,系统将会回收它。软引用通常用于对内存敏感的程序中。
软引用案例
- 总结:软引用的代码执行过程和弱引用大致相同。根据打印的内容就说明了当JVM内存充足的时候,JVM是不会考虑去回收软引用指向的内存空间的。我们可以注意到,在上面的代码中,有一行代码是为str = null,虽然这行代码是不能阻止垃圾回收器回收对象,但是可以延迟回收,这点和弱引用是不相同的。这也是软引用适合做缓存而弱引用适合存储元数据的根本原因所在。
- 一个使用弱引用的典型例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。
弱引用(WeakReference)
- 弱引用通过WeakReference类实现,弱引用和软引用很像但是弱引用的级别更低。
对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存(立即回收的方式)。当然并不是说当一个对象只有弱引用时,它就会立即被回收—-正如那些使用引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收。
public class Main {
public static void main(String[] args) {
String str = new String("Struts2权威指南");
//创建一个弱引用,让这个弱引用引用到“Struts2权威指南”字符串
WeakReference weakReference = new WeakReference(str);
//切断str引用和“Struts2权威指南”字符串之间的引用
str = null;
//取出弱引用所引用的对象
System.out.println(weakReference.get());
//强制进行垃圾回收
System.gc();
System.runFinalization();
//再次取出弱引用所引用的对象
System.out.println(weakReference.get());
}
} //执行的结果为:“Struts2权威指南”和null弱引用代码
由上面的代码我们可以看到,程序首先创建了一个“Struts2权威指南”对象,并且让str引用变量引用它。 WeakReference weakReference = new WeakReference(str); 时,系统首先创建了一个弱引用,和str指向同一个对应,当程序执行str = null;的时候,程序剪断了str和“Struts2权威指南”之间的联系,此时“Struts2权威指南”这时只有一个弱引用weakReference指向它,这个时候我们的程序依然可以通过这个弱引用来访问该字符串常量,程序的第一个System.out.println(weakReference.get())依然是可以输出“Struts2权威指南”的,接下来程序强制垃圾回收,如果系统垃圾回收器启动,那么将只有弱引用所引用的对象会被清除掉,当程序执行到第二个System.out.println(weakReference.get())的时候,通常输出的就只是null值了。
- 总结: 当我们将str赋值为空,该对象就可以被垃圾回收器回收。因为该对象此时不再含有其他强引用,即使指向该对象的弱引用WeakReference也没有办法阻止垃圾回收器对该对象的回收。相反的,如果该对象还有软引用,str不会被立即回收,除非JVM需要内存。
虚引用 (PhantomReference)
虚引用通过PhantomReference类实现,虚引用完全类似于没有引用。虚引用对对象本身没有太大的影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时。那它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)联合使用。
public class Main2 {
public static void main(String[] args) {
//创建一个字符串引用
String str = new String("Struts2权威指南");
//创建一个引用队列
ReferenceQueue referenceQueue = new ReferenceQueue();
//创建一个虚引用 让次虚引用引用到“Struts2权威指南”字符串
PhantomReference phantomReference = new PhantomReference(str, referenceQueue);
//切断str和“Struts2权威指南”之间的引用关系
str = null;
//取出虚引用所引用的对象,并不能通过虚引用访问被引用的对象,
//所以此处输出的应该是null
System.out.println(phantomReference.get());
//强制进行垃圾回收
System.gc();
System.runFinalization();
//取出引用队列最先进入队列中的引用于phantomReference进行比较
System.out.println(referenceQueue.poll() == phantomReference);
}
} 输出结果: null 和 true虚引用案例
- 因为系统无法通过虚引用来获取被引用的对象,所以在执行第一个System.out.println(phantomReference.get())的时候,打印出来的是空值(即使此时系统并未进行强制垃圾回收)。当程序强制垃圾回收后,只有虚引用引用的字符串对象将会被垃圾回收,当被引用的对象被回收后,对象的引用将被添加到关联的引用队列中去(也就时说,虚引用指向的字符串对象会被垃圾回收器回收,而自己本身将被添加到与之关联的引用队列中去),因此我们才在第二个System.out.println(referenceQueue.poll() == phantomReference)中看到输出的true
虚引用当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。由于Object.finalize()方法的不安全性、低效性,常常使用虚引用完成对象回收前的资源释放工作。参考我的另一篇博客:解释为什么finalize是不安全的,不建议使用
当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列. * 而此时PhantomReference所指向的对象并没有被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收.
引用队列(ReferenceQueue)(https://blog.csdn.net/cl534854121/article/details/80518070)
- 引用队列由java.lang.ref.ReferenceQueue类来表示,它用于保存被回收后对象的引用。当把软引用、弱引用和引用队列联合使用的时候,系统在回收被引用的对象之后,将把被回收对象对应的应用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象被释放之后,将把已经回收对象对应的虚引用添加它的关联引用队列中,这是得可以在对象被回收之前采取行动。
- 软引用和弱引用可以单独使用,但是虚引用不能单独使用,单独使用虚引用没有太大的意义,它必须和引用队列一块使用。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。由于Object.finalize()方法的不安全性、低效性,常常使用虚引用完成对象回收前的资源释放工作。参考博客:解释为什么finalize是不安全的,不建议使用
总结
- 在应用程序中,我们使用引用类可以边面在程序执行期间将对象留在内存中。如果我们一软引用,弱引用或虚引用的方式引用对象,这样垃圾收集器就能够随意的释放对象。如果希望尽可能的减少程序在其生命周期中所占的内存大小时,这些引用类就很有好处。
- 当然必须指出的一个问题: 要使用这些特殊的引用类,就不能保留对对象的强引用。如果保留了对对象的强引用,那么就会浪费这些类所提供的任何好处。
- 其实别的引用也可以在new的时候添加引用队列,只是弱引用和软引用是在应用对象gc之后添加到引用队列,而虚引用添加引用队列是在gc之前
- 四种引用最主要的区别就是垃圾回收器回收的时机不同:
- 强引用: 我们经常使用的一种引用。基本上垃圾回收器不会主动的去回收
- 软引用: 在JVM没有出现内存不足的情况下,垃圾回收器不会去主动回收软引用
- 弱引用: 垃圾回收器会立刻回收弱引用。
- 虚引用: 虚引用引用的字符串会被垃圾回收器回收, 自己本身会被添加到关联的引用队列中去。
java中的强,软,弱,虚引用的更多相关文章
- java中强,软,弱,虚引用 以及WeakHahMap
java中强,软,弱,虚引用 以及WeakHahMap 一:强软引用: 参考:http://zhangjunhd.blog.51cto.com/113473/53092/进行分析 packa ...
- Java中带包(创建及引用)的类的编译
Java中带包(创建及引用)的类的编译与调试 java源程序的编译大家都知道,也就是cmd中到源文件所在目录下javac **.java即可,当程序中有包声明还能简简单单的直接javac **.jav ...
- Java中到底是值传递还是引用传递?
Java中到底是值传递还是引用传递? 我们先回顾一下基本概念 实参和形参 参数在编程语言中是执行程序需要的数据,这个数据一般保存在变量中.在Java中定义一个方法时,可以定义一些参数, 举个例子: p ...
- Java中“指针”的解释以及对“引用”的理解
Java中"指针"的解释以及对"引用"的理解 初学Java面对对象编程,对于一些概念还真的有点难以理解,主要是因为不由自主的联系到以前学过的C语言知识,时不时的 ...
- java中的强引用(Strong reference),软引用(SoftReference),弱引用(WeakReference),虚引用(PhantomReference)
之前在看深入理解Java虚拟机一书中第一次接触相关名词,但是并不理解,只知道Object obj = new Object()类似这种操作的时候,obj就是强引用.强引用不会被gc回收直到gc roo ...
- java中的强引用、软引用、弱引用、虚引用
1.强引用(Strong Reference):指程序代码中普遍存在的,类似“Object obj = new Object()”这类的引用,只要对象存在强引用关联,JVM必定不会回收这个对象: 2. ...
- 详解Java中对象的软、弱和虚引用的区别
对于大部分的对象而言,程序里会有一个引用变量来引用该对象,这是最常见的引用方法.除此之外,java.lang.ref包下还提供了3个类:SoftReference.WeakReference和Phan ...
- Java中的强引用,软引用,弱引用
作者:winterSunshine链接:https://www.zhihu.com/question/37401125/answer/100981172来源:知乎著作权归作者所有.商业转载请联系作者获 ...
- java中的正则表达式捕获组与引用的概念
今天群里有个人问,怎样用增则表达式匹配三角形的三边,其实只是要匹配三个数字而已,如 301 402 503 开始认为很简单,我就写了一个 "(([1-9]\\d?)\\s){2}$2&q ...
随机推荐
- vertical起作用的条件
只有当display为行内块元素时(inline-block),vertical-align:middle:才生效,或者将display设置为table-cell,需要多行文字居中时才需要,单行文字, ...
- NYOJ 6:喷水装置(一)(贪心)
6-喷水装置(一) 内存限制:64MB 时间限制:3000ms 特判: No 通过数:68 提交数:111 难度:3 题目描述: 现有一块草坪,长为20米,宽为2米,要在横中心线上放置半径为Ri的喷水 ...
- int &p
int &p为引用,而int p为定义变量.二者区别如下:1 引用在定义的时候必须赋值,否则编译会出错.正确的形式为int &p = a;其中a为int型变量.2 引用在定义时不会分配 ...
- 《DSP using MATLAB》Problem 5.11
代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ %% Output In ...
- easyui的select使用
这是一个小demo,其他相关功能查看easyuiAPI,需要注意的是valueField: 'id', textField: 'name'中id指json的key,name指json的值,id和nam ...
- 【HAOI2008】圆上的整点
数学题 原题:平面上有一个圆, 圆心坐标为(0,0),半径为n. 问圆周上有多少个整点. 整点的定义即x,y坐标均为整数的点. 这根本就是一道数学题,注意是数学题,不是数论,数学! 纯粹就看魔性变公式 ...
- Java打印九九乘法表及倒打九九乘法表
//正打 public class Test3 { public static void main(String[] args) { for(int j=1;j<10;j++){ for(int ...
- Cassandra基础3
cassandra读性能优化:1.禁用read repair每次读操作,无论读请求设置读一个节点还是多个节点,cassandra返回给客户端最新的数据后,都会后台对比所有副本的数据并对差异数据进行修复 ...
- jsonDB使用手冊
已在github上建立项目:https://github.com/ThinkerCodeChina/jsonDB 博客:http://blog.csdn.net/thinkercode/ 简单介绍: ...
- Unity3d插件开发与SDK对接实战 学习
c++: 注意x86/x64,vs2015. #include "stdafx.h" extern "C" { int Add(int a, int b) { ...