一次对ThreadLocal的学习引发的思考

ThreadLocal对Entry的引用是弱引用,于是联想到四种引用的生命周期。

  1. 强引用,不会进行垃圾回收
  2. 软引用,JVM内存不够,进行回收
  3. 弱引用,下次GC,直接进行回收
  4. 虚引用,不会对GC产生任何影响,结合ReferenceQueue使用,只为引用被回收时收到通知。

所以如果只有弱引用指向ThreadLocal,那么下次GC,ThreadLocal将被回收。阿里的代码规范上也要求ThreadLocal要被static修饰,就是为了防止后续还要使用,但ThreadLocal已被GC回收。

然后为了测试弱引用被GC写了下面的代码:

	@Test
public void test() throws InterruptedException {
ReferenceQueue queue = new ReferenceQueue(); Thread thread = new Thread(() -> {
while (true){
Object obj;
if((obj = queue.poll())!= null){
System.out.println("queue!!! " + obj);
}
}
}); thread.start();
Reference reference1 = new Reference();
WeakReference reference = new WeakReference(reference1, queue);
System.out.println(reference);
reference1 = null; System.gc(); thread.join(); } private static class Reference { @Override
protected void finalize() throws Throwable {
System.out.println("finalize!!!"+this);
}
}

输出如下:

java.lang.ref.WeakReference@200a570f
queue!!! java.lang.ref.WeakReference@200a570f
finalize!!!ConnectionTest$Reference@74560fd0

queue比finalize先输出,开始我以为是io竞争,忽视不理,将弱引用改为虚引用,这时输出为:

java.lang.ref.PhantomReference@200a570f
finalize!!!ConnectionTest$Reference@74560fd0

queue一直不输出,这时我怀疑是我代码问题,于是在网上百度了别人的例子改了下,可以正常输出queue,我又怀疑是我自定义类的问题,于是将原来代码虚引用改为String

PhantomReference reference = new PhantomReference(new String(), queue);

正常输出,what???但是我的自定义类没什么东西啊,只有一个finalize(),难道它会影响引用,但弱引用没问题,虚引用就有问题???于是我将虚引用改为软引用,然后创建大量的数组,正常输出。

于是我怀疑finalize对虚引用有什么影响,难道找到bug了,我百度了finalize和虚引用,然后找到以下文章:

JAVA虚引用为什么在重载finalize后不会在回收时被置入引用队列?

finalize方法执行过程

详细可以看:How to Handle Java Finalization's Memory-Retention Issues

  1. 重写了finalize()的类实例化时,JVM会标记该对象为finalizable
  2. GC thread检测到对象不可达时,如果对象是finalizable,会将对象添加到finalization queue,对象重新可达,推迟GC
  3. finalizer thread在一段时间之后,将会从finalization queue出队对象,调用对象的finalize(),随后标记对象为finalized
  4. GC thread重新检测到对象不可达,这时才回收对象。

看到这也就明白的虚引用在重写了finalize()之后为啥不输出queue,要经过两次GC,对象才会被回收,这时才进入Reference Queue,将代码改动如下:

	@Test
public void test5() throws InterruptedException {
Reference reference = new Reference();
ReferenceQueue referenceQueue = new ReferenceQueue();
Thread thread = new Thread(() -> {
while (true){
Object obj;
if((obj = referenceQueue.poll())!= null){
System.out.println("queue!!! "+obj);
}
}
}); thread.start();
PhantomReference reference1 = new PhantomReference(reference, referenceQueue);
reference = null;
System.gc();
Thread.sleep(1000);
System.gc();
thread.join();
}

输出如下:

finalize!!!ConnectionTest$Reference@74560fd0
queue!!! java.lang.ref.PhantomReference@5b708109

jstat也可以看到进行了两次的GC差别:

不输出queue的gc情况:

输出queue的gc情况:

虚引用和弱引用进入Reference Queue时机

上面已经解释了虚引用为啥不输出queue的原因,但为啥弱引用只经过一次gc就输出了queue的??

JAVA虚引用为什么在重载finalize后不会在回收时被置入引用队列?

弱引用、虚引用、finalize实践,及它们的顺序

这两篇文章提到:

  1. 弱引用:一旦对象只有弱引用,GC是会把弱引用直接插入引用队列,与插入finalization queue是同一时机。
  2. 虚引用:要在对象正式被回收,才进入引用队列

此外,我还发现jdk8虚引用不用调用clear()清除referent对象,要在引用队列中手动清除。

jdk8 PhantomReference注释如下:

* Unlike soft and weak references, phantom references are not
* automatically cleared by the garbage collector as they are enqueued. An
* object that is reachable via phantom references will remain so until all
* such references are cleared or themselves become unreachable.

finalize()和四种引用的一点思考的更多相关文章

  1. Java虚拟机15:再谈四种引用状态

    JVM的四种引用状态 在Java虚拟机5:Java垃圾回收(GC)机制详解一文中,有简单提到过JVM的四种引用状态,当时只是简单学习,知道有这么一个概念,对四种引用状态理解不深.这两天重看虚拟机这部分 ...

  2. Java中的四种引用

    引用定义 实际上,Java中存在四种引用,它们由强到弱依次是:强引用.软引用.弱引用.虚引用.下面我们简单介绍下这四种引用: 强引用(Strong Reference):通常我们通过new来创建一个新 ...

  3. Java虚拟机19:再谈四种引用状态

    JVM的四种引用状态 在Java虚拟机5:Java垃圾回收(GC)机制详解一文中,有简单提到过JVM的四种引用状态,当时只是简单学习,知道有这么一个概念,对四种引用状态理解不深.这两天重看虚拟机这部分 ...

  4. JAVA中的四种引用以及ReferenceQueue和WeakHashMap的使用示例

    简介: 本文主要介绍JAVA中的四种引用: StrongReference(强引用).SoftReferenc(软引用).WeakReferenc(弱引用).PhantomReference(虚引用) ...

  5. java四种引用及在LeakCanery中应用

    java 四种引用 Java4种引用的级别由高到低依次为: StrongReference > SoftReference > WeakReference > PhantomRefe ...

  6. JAVA四种引用方式

    JAVA四种引用方式: java.lang.ref: 强引用(直接变量赋值) 软引用(SoftReference): 只有在要发生OOM错误之前才会回收掉老的软引用对象,应用场景主要防止内存溢出.(缓 ...

  7. JAVA不可不知的强软弱虚四种引用

    一个变量指向new对象,就是引用,在java中有四种引用,分别是强软弱虚,常见的Object o = new Object(),就是强引用,垃圾回收的时候,强引用不会被回收.   公用类: publi ...

  8. Java中四种引用:强、软、弱、虚引用

    这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...

  9. Java四种引用包括强引用,软引用,弱引用,虚引用。

    Java四种引用包括强引用,软引用,弱引用,虚引用. 强引用: 只要引用存在,垃圾回收器永远不会回收Object obj = new Object();//可直接通过obj取得对应的对象 如obj.e ...

随机推荐

  1. mysql replace替换某字段的值

    由于最近我们的一个网站回购了一个很好的域名所有与之相关的项目都需要修改: 今天接到一个任务将我们会员开通的个人网站的二级域名换成新域名,看了一下库已开通的还很少才2w多,且要换的和之前库中的数据很规则 ...

  2. BERT的前世今生

    Transformer Transformer来自论文: All Attention Is You Need 别人的总结资源: 谷歌官方AI博客: Transformer: A Novel Neura ...

  3. 发布Nuget包时遇到都意外

    准备好工具和发布教程.(这些网上都有,我就不说了,就说说我遇到都意外.) 在发布包都过程中,我给我都dll命名为Common.不知道是不是这个原因导致的我包发布上去后,程序对其引用时居然没主动引用进程 ...

  4. 【未完待续】MVC 之HTML辅助方法

    顾名思义,HTML辅助方法(HTML Helper)就是用来辅助产生HTML之用, 在开发View的时候一定会面对许多HTML标签,处理这些HTML的工作非常繁琐,为了降低View的复杂度,可以使用H ...

  5. css div如何隐藏?

    在我们平时布局网站的时候,想要把div进行隐藏,但是很多人不知道css控制div显示隐藏?下面我们来讲解一下css如何让div隐藏. 1.使用display:none来隐藏div 我们可以使用disp ...

  6. HTML5(八)Web Workers

    HTML 5 Web Workers web worker 是运行在后台的 JavaScript,不会影响页面的性能. 什么是 Web Worker? 当在 HTML 页面中执行脚本时,页面的状态是不 ...

  7. 用Serverless Kubernetes为.NET (Core)应用保驾护航

    前言 容器化对现在(0202年)来说,已经不算是什么新东西了,老黄最近也在公司推动这一块的发展,有幸落地了几个项目,有.NET Core的,也有.NET Framework的. 容器化现在主流的就是d ...

  8. 自动化测试平台(Vue前端框架安装配置)

    Vue简介: 通俗的来说Vue是前端框架,用来写html的框架,可轻量级也可不轻量级 Vue特性: 绑定性,响应性,实时性,组件性 安装软件以及控件: 控件库:element-ui node.js ( ...

  9. Tensorflow-gpu环境搭建

    显卡设备:英伟达1060 系统:Windows10 python版本:3.7.4 CUDA:cuda_10.0.130_411.31_win10 cudnn:cudnn-10.0-windows10- ...

  10. Let's GO(二)

    人生苦短,Let's GO Let's GO(一) Let's GO(二) Let's GO(三) Let's GO(四) 今天我学了什么? 1. Map map:映射,使用散列表(hash)实现 m ...