【案例演示】JVM之强引用、软引用、弱引用、虚引用
1.背景
想要理解对象什么时候回收,就要理解到对象引用这个概念,于是有了下文
2.java中引用对象结构图

3.引用详解
3.1.什么是强引用
a.当内存不足,JVM开始垃圾回收,对于强引用的对象,就算是出现了00M也不会对该对象进行回收,死都不收。
b.强引用是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。
在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。
当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到JVM也不会回收。
因此强引用是造成Java内存泄漏的主要原因之一
c.对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,一般认为就是可以被垃圾收集的了〈当然具体回收时机还是要看垃圾收集策略)。
案例:

package com.wfd360.demo03GC.referDemo; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 12:12
* @description
*/
public class StrongRefer {
/**
* 强引用的理解
*
* @param args
*/
public static void main(String[] args) {
Object obj1 = new Object();
// 建立强引用
Object obj2 = obj1;
// 观察obj1 和 obj2 的各种内存地址
System.out.println("obj1=" + obj1);
System.out.println("obj2=" + obj2);
// obj1创建可以回收的条件
obj1 = null;
// gc回收
System.gc();
// 观察各对象情况
System.out.println("obj1=" + obj1);
System.out.println("obj2=" + obj2);
}
}

从测试结果课程看出,obj1的实际对象别没有回收;
3.2.什么是软引用
a.软引用是用来描述一些还有用但并非必需的对象,需要用java.lang.ref.SoftReference类来实现。
b.对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后,提供了Soft Reference类来实现软引用。
c.软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收!
案例:

package com.wfd360.demo03GC.referDemo; import java.lang.ref.SoftReference; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 12:12
* @description
*/
public class SoftRefer { /**
* 软引用的理解
* 通过设置jvm参数,在不同的条件下观察
*
* @param -Xms5m -Xmx5m -XX:+PrintGCDetails
* @param args
*/
public static void main(String[] args) {
// 测试内存充足(不回收软引用)
//testSoftReferNOGc();
// 测试内存不充足(回收软引用)
testSoftReferGc();
} /**
* 模拟内存充足的情况
*/
public static void testSoftReferNOGc() {
Object obj1 = new Object();
// 建立软引用
SoftReference softRefer = new SoftReference<>(obj1);
// 观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
// obj1创建可以回收的条件
obj1 = null;
// gc回收
System.gc();
// 再次观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
} /**
* 模拟内存不足
* 1.设置较小的堆内存
* 2.创建大对象
* 3.jvm参
* -Xms5m -Xmx5m -XX:+PrintGCDetails
*/
public static void testSoftReferGc() {
Object obj1 = new Object();
// 建立软引用
SoftReference softRefer = new SoftReference<>(obj1);
// 观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
// obj1创建可以回收的条件
obj1 = null;
try {
byte[] bytes = new byte[6 * 1024 * 1024];
} catch (Throwable e) {
System.out.println("===============>error:" + e.getMessage());
} finally {
// 再次观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
}
}
}
内存充足测试结果:

内存不充足测试结果:

实际案例
假如有一个应用需要读取大量的本地数据(图片、通讯率、临时文件等):
如果每次读取数据都从硬盘读取则会严重影响性能,
如果一次性全部加载到内存中又可能造成内存溢出。
此时使用软引用可以解决这个问题。
设计思路是:用一个HashMap来保存数据的路径和相应数据对象关联的软引用之间的映射关系,在内存不足时,
JVM会自动回收这些缓存数据对象所占用的空间,从而有效地避免了00M的问题。
Map<String,SoftReference>imageCache=new HashMap<String,SoftReference>();
3.3.什么是弱引用
a.弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。
b..当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供广Weak Reference类来实现弱引用。
c.弱引用需要用Java.lang.ref.WeakReference类来实现,它比软引用的生存期更短.
案例:

package com.wfd360.demo03GC.referDemo; import java.lang.ref.WeakReference; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 12:12
* @description
*/
public class WeakRefer { /**
* 弱引用的理解
*
* @param args
*/
public static void main(String[] args) {
Object obj1 = new Object();
// 建立弱引用
WeakReference softRefer = new WeakReference<>(obj1);
// 观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
// obj1创建可以回收的条件
obj1 = null;
// gc回收
System.gc();
// 再次观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
} }

扩展知识-WeakHashMap
查看API介绍:

测试代码:

package com.wfd360.demo03GC.referDemo; import java.util.HashMap;
import java.util.WeakHashMap; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 5:10
* @description <p>
* 弱引用引用之:WeakHashMap
* 以弱键 实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。
* 更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,
* 然后被回收。丢弃某个键时,其条目从映射中有效地移除,因此,该类的行为与其他的 Map 实现有所不同。
* </p>
*/
public class WeakReferMap {
/**
* 测试 HashMap 与 WeakHashMap 区别
* 测试逻辑:
* 1.创建不同的map
* 2.创建key value值
* 3.放入各自的map,并打印结果
* 4.将key设置为null,并打印结果
* 5.手动GC,并打印结果
*
* @param args
*/
public static void main(String[] args) {
hashMapMethod();
System.out.println("--------华丽的分割线--------");
weakHashMapMethod();
} /**
* HashMap测试(强引用)
*/
private static void hashMapMethod() {
HashMap<String, String> map = new HashMap<>();
String key = "key1";
String value = "HashMap-value"; map.put(key, value);
System.out.println(map); key = null;
System.out.println(map); System.gc();
System.out.println(map);
} /**
* 若引用(WeakHashMap测试)
*/
private static void weakHashMapMethod() {
WeakHashMap<String, String> map = new WeakHashMap<>();
// 注意这里的new一个字符串与直接写key="key2"对测试结果是有区别的,详细原因可以看之前讲的内存分配
String key = new String("key2");
String value = "WeakHashMap-value"; map.put(key, value);
System.out.println(map); key = null;
System.out.println(map); System.gc();
System.out.println(map); } }
测试结果:

从测试结果可以看出:弱引用的map数据已经被回收。
扩展知识-ReferenceQueue引用队列

代码:

package com.wfd360.demo03GC.referDemo; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 7:23
* @description
*/
public class QueueRefer {
/**
* 测试弱引用回收前,把数据放入队列中
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Object obj1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue();
// 当GC释放对象内存的时候,会将引用加入到引用队列
WeakReference<Object> weakReference = new WeakReference<>(obj1, referenceQueue); System.out.println(obj1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll()); System.out.println("--------华丽的分割线--------");
obj1 = null;
System.gc();
Thread.sleep(500); System.out.println(obj1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
} }
采用弱引用的方式测试结果:

从测试结果可以看出,需要回收的对象已经进入队列。
采用软引用的方式测试结果:

从测试结果可以看出,软引用,没有到达回收的条件,并没有进行回收,也不会进入队列;
3.4.什么是虚引用
1.虚引用需要java.lang.ref.PhantomReference类来实现。
2.与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有
虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访
问对象,虚引用必须和引用队列(ReferenceQueue)联合使用。
3.虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的
机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象。其意义在于说明一个对象己
经进入俑finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。
4.设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加
进一步的处理。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
代码:

package com.wfd360.demo03GC.referDemo; import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 7:44
* @description
*/
public class PhantomRefer {
/**
* 虚引用测试
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Object obj1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue();
PhantomReference<Object> phantomReference = new PhantomReference<>(obj1,referenceQueue); System.out.println(obj1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll()); System.out.println("--------华丽的分割线--------"); obj1 = null;
System.gc();
Thread.sleep(500); System.out.println(obj1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
} }
测试结果:

4.重要总结
对象是否存活判断流程:
1.可达性分析,看是否有GC Roots的引用链,如果没有将做第一次标记;
2.检查是否需要执行finalize()方法,
如果没必要(之前执行过了),直接回收内存;
如果要执行finalize()方法,这个时候对象如果再次建立引用链(唯一自救机会),对象不会被回收,否则直接回收;
总结:
1.对象回收满足两个条件:
a.没有引用链。
b.回收前会执行finalize()方法,如果执行finalize(),没有再次建立连接(如果重新与引用链上的任意对象建立连接,例如给对象赋值,该对象都不会被回收)
2.在gc回收前会执行finalize()方法,只执行一次,并且是异步执行不保证执行成功,线程优先级低
代码演示:

package com.wfd360.demo03GC.referDemo; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 8:34
* @description
*/
public class FinalizeGC {
public static FinalizeGC obj1 = null; /**
* 重写finalize方法
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("执行finalize方法");
// 自救,在回收时建立引用链
FinalizeGC.obj1 = this;
} public static void main(String[] args) throws InterruptedException {
obj1 = new FinalizeGC(); obj1 = null;
System.gc();
Thread.sleep(600);
System.out.println("第一次自救成功:"+obj1); obj1 = null;
System.gc();
Thread.sleep(600);
System.out.println("第二次自救失败,不会再次执行finalize方法:"+obj1);
}
}
测试结果:

完美!
【案例演示】JVM之强引用、软引用、弱引用、虚引用的更多相关文章
- Java中四种引用:强、软、弱、虚引用
这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...
- 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...
- Java:对象的强、软、弱、虚引用
转自: http://zhangjunhd.blog.51cto.com/113473/53092 1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...
- Java:对象的强、软、弱和虚引用
1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK ...
- Java对象的强、软、弱和虚引用详解
1.对象的强.软.弱和虚引用 转自:http://zhangjunhd.blog.51cto.com/113473/53092/ 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...
- java基础知识再学习--集合框架-对象的强、软、弱和虚引用
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://zhangjunhd.blog.51cto.com/113473/53092 本文 ...
- Java对象的强、软、弱和虚引用原理+结合ReferenceQueue对象构造Java对象的高速缓存器
//转 http://blog.csdn.net/lyfi01/article/details/6415726 1.Java对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变 ...
- Java:对象的强、软、弱和虚引用[转]
原文链接:http://zhangjunhd.blog.51cto.com/113473/53092/ 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法 ...
- 详解Java中对象的软、弱和虚引用的区别
对于大部分的对象而言,程序里会有一个引用变量来引用该对象,这是最常见的引用方法.除此之外,java.lang.ref包下还提供了3个类:SoftReference.WeakReference和Phan ...
- Java对象的强、软、弱和虚引用+ReferenceQueue
Java对象的强.软.弱和虚引用+ReferenceQueue 一.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足 ...
随机推荐
- SQL查找大小为n的连续区间
数据准备 create table sequence ( seq int not null primary key ); insert into values(3); insert into valu ...
- matlab自学笔记
1.字符串格式化,用sprintf如a=sprintf('%.2f_除以%d等于%.3f',1.5,2,0.75)%则a=1.50除以2等于0.750 2.for循环只能针对整数,不能遍历字符串或其他 ...
- 关于安装Linux-centOS时遇到的问题
1.新建虚拟机实例后倒入centos镜像开机报错.提示不支持 64 位.... 重新下载虚拟机安装包,重新安装. 2.安装到检查光盘镜像的下一步,vm is nor support (or ... c ...
- 一、Redis 总结
官网 Redis 介绍 Redis 是一个开源的.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的 API. Redis 是一个 key-value 存储系统.为了 ...
- Kubernetes笔记(四):详解Namespace与资源限制ResourceQuota,LimitRange
前面我们对K8s的基本组件与概念有了个大致的印象,并且基于K8s实现了一个初步的CI/CD流程,但对里面涉及的各个对象(如Namespace, Pod, Deployment, Service, In ...
- 【HBase】知识小结+HMaster选举、故障恢复、读写流程
1:什么是HBase HBase是一个高可靠性,高性能,面向列,可伸缩的分布式数据库,提供海量数据存储功能,一个结构化的分布式存储系统,不同于一般的关系型数据库,它适合半结构化和非结构化数据存储. 2 ...
- MyBatis(一) 概述与SQL定制、对象映射
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.MyBatis概述 1.mybatis简介 MyBatis 是支持定制化 SQL.存储过程以及高级映 ...
- Vue父子组件传值以及父调子方法、子调父方法
稍微总结了一下Vue中父子间传值以及相互调方法的问题,非常基础.希望可以帮到你!先来个最常用的,直接上代码: 1.父传值给子组件 父组件: <template> <div> & ...
- Java实现 LeetCode 98 验证二叉搜索树
98. 验证二叉搜索树 给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数. 节点的右子树只包含大于当前节点的数. 所有左子树和右 ...
- 用户管理命令-passwd
passwd可以给用户设置密码或者修改密码,超级用户可以修改任何用户的密码,而且可以不遵守密码的复杂性原则,普通用户只能修改自己的密码,必须遵守密码的复杂性原则 passwd [选项] 用户名 常用选 ...