java强引用、软引用、弱引用、虚引用以及FinalReference
基于JDK1.8 rt.jar是java中的基础类库,在它的 java.lang.ref包下,有着一堆关于引用的类。软引用、弱引用以及虚引用的定义就在其中。另外还有一个FinalReference。
* ReferenceQueue//引用队列
* Reference(abstract)//抽象类
* SoftReference//软引用
* WeakReference//弱引用
* PhantomReference//虚引用
* FinalReference
*Finalizer
*FinalizerHistogram
强引用
jdk1.8的包中并没有这个类,是普通类的引用。在Hotspot的垃圾回收过程中,采用可达性分析算法判定对象是否存活,可以到达GC Roots的对象是永远不会被当做垃圾回收的;
软引用(SoftReference)
软引用常常被用于实现一些高速且敏感的缓存,如Guava中的LocalCache中就是用了SoftReference来做缓存 它的特点是:如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
弱引用(WeakReference)
弱引用同样经常被用做缓存的实现,在JDK中比较常见的是维护一种非强制性的映射关系的实现,动态代理的缓存就是基于它实现的。 它的特点是:在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。可见它相对于软引用来说,拥有更为短暂的生命周期。
虚引用(PhantomReference)
如果一个对象只被一个PhantomReference所引用,那等于没有任何引用。它随时都可以 回收。虚引用经常被用作 跟踪对象被垃圾回收的活动。
以上三种引用类型,均可以和ReferenceQueue引用队列一起使用,例如:
ReferenceQueue referenceQueue = new ReferenceQueue();//引用队列
Object obj = new Object();//强引用
WeakReference weakReference = new WeakReference(obj,referenceQueue);//初始化弱引用,PhantomReference只有这一个构造函数
一个对象,通过被“弱引用对象”引用,而改变了它的生命周期。可以方便它更快的被垃圾回收。试想在一个map构造的缓存系统中,缓存值是一直被map强引用着的,map值一直被使用,所有的缓存值也会一直得不到释放,这是很危险的,随时都会带来OOM的危险,这时,Reference就派上用场了:通过一个软引用或者虚引用隔离开map对象和缓存值的直接引用,变成map->reference->缓存值,一旦缓存值没有强引用,就可以很快被回收了。
具体看下如下实验(一种非强制性映射关系的简单实现):
public class Oil {
private String name;
public Oil(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Oil "+name+" finalize.");
}
@Override
public String toString() {
return "Oil "+name+" print!";
}
}
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class ReferenceQueueTest {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Oil> oilReferenceQueue = new ReferenceQueue<>();
Map map = new HashMap();
Oil oil = new Oil("花生油");
Oil oil1 = new Oil("葵花籽油");
WeakReference<Oil> oilWeakReference = new WeakReference<>(oil, oilReferenceQueue);
WeakReference<Oil> oilWeakReference1 = new WeakReference<>(oil1);
map.put(oilReferenceQueue,oilWeakReference1);
while (true) {
System.out.println(oilWeakReference);
System.out.println(oilWeakReference1);
System.out.println(oilWeakReference.get());
System.out.println(oilWeakReference1.get());
Reference<? extends Oil> reference = null;
while ((reference = oilReferenceQueue.poll()) != null) {
System.out.println("ReferenceQueue内:" + reference);
map.remove(reference);
return;
}
oil = null;
System.out.println("=========gc===========");
System.gc();//调用GC
TimeUnit.SECONDS.sleep(10);
}
}
}
打印结果:
"C:Program FilesJavajdk1.8.0_221binjava.exe" **-XX:+PrintGCDetails** "...
java.lang.ref.WeakReference@2503dbd3
java.lang.ref.WeakReference@4b67cf4d
Oil 花生油 print!
Oil 葵花籽油 print!
=========gc===========
[GC (System.gc()) [PSYoungGen: 3334K->744K(38400K)] 3334K->752K(125952K), 0.0008666 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 744K->0K(38400K)] [ParOldGen: 8K->647K(87552K)] 752K->647K(125952K), [Metaspace: 3228K->3228K(1056768K)], 0.0049257 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Oil 花生油 finalize.
java.lang.ref.WeakReference@2503dbd3
java.lang.ref.WeakReference@4b67cf4d
null
Oil 葵花籽油 print!
ReferenceQueue内:java.lang.ref.WeakReference@2503dbd3
Heap
PSYoungGen total 38400K, used 3661K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
eden space 33280K, 11% used [0x00000000d5e00000,0x00000000d6193500,0x00000000d7e80000)
from space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
to space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
ParOldGen total 87552K, used 647K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
object space 87552K, 0% used [0x0000000081a00000,0x0000000081aa1e00,0x0000000086f80000)
Metaspace used 3770K, capacity 4540K, committed 4864K, reserved 1056768K
class space used 416K, capacity 428K, committed 512K, reserved 1048576K
Process finished with exit code 0
那么,ReferenceQueue是做什么用呢?
如果缓存值被回收了,那么引用对象(也就是WeakReference或者SoftReference的对象)也就没用了,失去被应用对象的它们,会被放入ReferenceQueue中,方便更快回收。JDK中常见的回收代码如下:
private void expungeStaleEntries() {
CacheKey<K> cacheKey;
while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {//从队列拉出没用的引用类型对象
cacheKey.expungeFrom(map, reverseMap);//将它们从map中剔除,方便被垃圾回收
}
}
FinalReference
FinalReference和Finalizer以及常见的finnalize()方法应该放在一起说。
- FinalReference灰常简单(就一个传统的引用类都有的构造),我们看下他的子类:Finalizer。 这个类是和FinalReference一样,都是Package-private,只能在包内使用。 说明它是被jvm控制的。
- 那finnalize()是个怎么回事呢? java不同于C和C++,它的垃圾回收自然有垃圾回收器负责。 但也有比较特殊的情况,比方说你建立了一个连接,垃圾回收器只能释放掉那些经由new分配的内存区域,而对于建立的连接,则无所应对。 为了应对这种情况,java允许在类中定义一个名为finnalize()的方法。 它的工作原理是: 一旦垃圾回收器准备释放该对象占据的存储空间,会首先调用其finalize()方法,并且在下一次垃圾回收动作发生的时候,才会真正回收该对象占用的内存。 但请注意,finalize()并算不上java的“析构”。
- 它们的联系?
- 虚拟机在类加载的时候会将实现了finalize()方法的类成为“f类”。
- JVM在新建F类的对象时,调用Finalizer.register方法。 register方法,会新建一个指向该对象的强引用,这样它就不会被垃圾回收了。
- 在Finalizer类中,有一个static代码块:
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
finalizer.setDaemon(true);
finalizer.start();
}
private static class FinalizerThread extends Thread {
private volatile boolean running;
FinalizerThread(ThreadGroup g) {
super(g, "Finalizer");
}
public void run() {
// in case of recursive call to run()
if (running)
return;
// Finalizer thread starts before System.initializeSystemClass
// is called. Wait until JavaLangAccess is available
while (!VM.isBooted()) {
// delay until VM completes initialization
try {
VM.awaitBooted();
} catch (InterruptedException x) {
// ignore and continue
}
}
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
try {
Finalizer f = (Finalizer)queue.remove();//从引用队列取出Finalizer类
f.runFinalizer(jla);// 从Finalizer的引用中剥离对象,并且调用该对象的finalize()方法
} catch (InterruptedException x) {
// ignore and continue
}
}
}
}
它实现了一个守护线程,如注释所示,它将整个处理finalize的处理过程串联起来了。但是由于优先级并不是最高,所以当CPU资源不充足的时候,它的执行就会收到影响,也就不能保证你的连接一定被释放了。在《java编程思想》中,作者曾经写到:如果希望进行除释放存储空间之外的清理工作,还是得明确调用某个恰当的Java方法。这就等同与使用析构函数了,只是没有它方便。
- 传统数据库也能实现区块链存储
- golang(Go语言) byte/[]byte 与 二进制形式字符串 互转
- Sublime Text 最新注册码分享
- Lua table之弱引用
- 看吧,这就是现代化 PHP 该有的样子
- 从web图片裁剪出发:了解H5中的Blob
- Android子线程更新UI主线程方法之Handler
- Drawable.Bitmap.Canvas.Paint.Matrix
- 关于JSON.stringify和Unicode编码,需要注意的几点
- 用 PHP 的方式实现的各类算法合集
- Nginx 反向代理解决前后端联调跨域问题
- JavaScript对象length
- Go1.8.4和Go1.9.1版本发布
- Javascript数组操作
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Spring框架源码分析(IoC):BeanFactory和ApplicationContext容器家族
- spring框架通过xml以及注解方式注册BeanDefinition的流程全链路分析
- Java后端面试学习知识总结——GC
- Java后端面试学习知识总结——JVM
- Spring JDBC 框架,我的学习笔记
- 磁盘扩容
- 如何在MySQL中创建存储过程
- 磁盘扩容
- PyCharm安装疯狂打字机插件
- Java Lombok 常用注解
- Python 基础 安装 简单的输入输出 运行一个py程序
- Python 基础 数据类型 变量常量
- Java 快速排序 关于起始方向的选择问题 为什么一定要从右边开始
- Java 使用异或进行数组元素交换时的坑 返回0的原因
- Spring BindingResult获取不到结果可能的原因之一 参数顺序 没有紧挨着校验参数